Merge "Ensure local settings caches are not stale" into nyc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
f71d7feef2
@@ -598,6 +598,7 @@ aidl_files := \
|
|||||||
frameworks/base/core/java/android/net/Uri.aidl \
|
frameworks/base/core/java/android/net/Uri.aidl \
|
||||||
frameworks/base/core/java/android/net/NetworkRequest.aidl \
|
frameworks/base/core/java/android/net/NetworkRequest.aidl \
|
||||||
frameworks/base/core/java/android/net/LinkAddress.aidl \
|
frameworks/base/core/java/android/net/LinkAddress.aidl \
|
||||||
|
frameworks/base/core/java/android/util/MemoryIntArray.aidl \
|
||||||
frameworks/base/core/java/android/view/Display.aidl \
|
frameworks/base/core/java/android/view/Display.aidl \
|
||||||
frameworks/base/core/java/android/view/InputDevice.aidl \
|
frameworks/base/core/java/android/view/InputDevice.aidl \
|
||||||
frameworks/base/core/java/android/view/InputEvent.aidl \
|
frameworks/base/core/java/android/view/InputEvent.aidl \
|
||||||
|
|||||||
@@ -32491,7 +32491,6 @@ package android.provider {
|
|||||||
field public static final java.lang.String RADIO_WIFI = "wifi";
|
field public static final java.lang.String RADIO_WIFI = "wifi";
|
||||||
field public static final java.lang.String SHOW_PROCESSES = "show_processes";
|
field public static final java.lang.String SHOW_PROCESSES = "show_processes";
|
||||||
field public static final java.lang.String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
|
field public static final java.lang.String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
|
||||||
field public static final java.lang.String SYS_PROP_SETTING_VERSION = "sys.settings_global_version";
|
|
||||||
field public static final java.lang.String TRANSITION_ANIMATION_SCALE = "transition_animation_scale";
|
field public static final java.lang.String TRANSITION_ANIMATION_SCALE = "transition_animation_scale";
|
||||||
field public static final java.lang.String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled";
|
field public static final java.lang.String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled";
|
||||||
field public static final java.lang.String USE_GOOGLE_MAIL = "use_google_mail";
|
field public static final java.lang.String USE_GOOGLE_MAIL = "use_google_mail";
|
||||||
@@ -32571,7 +32570,6 @@ package android.provider {
|
|||||||
field public static final java.lang.String SELECTED_INPUT_METHOD_SUBTYPE = "selected_input_method_subtype";
|
field public static final java.lang.String SELECTED_INPUT_METHOD_SUBTYPE = "selected_input_method_subtype";
|
||||||
field public static final java.lang.String SETTINGS_CLASSNAME = "settings_classname";
|
field public static final java.lang.String SETTINGS_CLASSNAME = "settings_classname";
|
||||||
field public static final java.lang.String SKIP_FIRST_USE_HINTS = "skip_first_use_hints";
|
field public static final java.lang.String SKIP_FIRST_USE_HINTS = "skip_first_use_hints";
|
||||||
field public static final java.lang.String SYS_PROP_SETTING_VERSION = "sys.settings_secure_version";
|
|
||||||
field public static final java.lang.String TOUCH_EXPLORATION_ENABLED = "touch_exploration_enabled";
|
field public static final java.lang.String TOUCH_EXPLORATION_ENABLED = "touch_exploration_enabled";
|
||||||
field public static final deprecated java.lang.String TTS_DEFAULT_COUNTRY = "tts_default_country";
|
field public static final deprecated java.lang.String TTS_DEFAULT_COUNTRY = "tts_default_country";
|
||||||
field public static final deprecated java.lang.String TTS_DEFAULT_LANG = "tts_default_lang";
|
field public static final deprecated java.lang.String TTS_DEFAULT_LANG = "tts_default_lang";
|
||||||
@@ -32686,7 +32684,6 @@ package android.provider {
|
|||||||
field public static final deprecated java.lang.String SHOW_WEB_SUGGESTIONS = "show_web_suggestions";
|
field public static final deprecated java.lang.String SHOW_WEB_SUGGESTIONS = "show_web_suggestions";
|
||||||
field public static final java.lang.String SOUND_EFFECTS_ENABLED = "sound_effects_enabled";
|
field public static final java.lang.String SOUND_EFFECTS_ENABLED = "sound_effects_enabled";
|
||||||
field public static final deprecated java.lang.String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
|
field public static final deprecated java.lang.String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
|
||||||
field public static final java.lang.String SYS_PROP_SETTING_VERSION = "sys.settings_system_version";
|
|
||||||
field public static final java.lang.String TEXT_AUTO_CAPS = "auto_caps";
|
field public static final java.lang.String TEXT_AUTO_CAPS = "auto_caps";
|
||||||
field public static final java.lang.String TEXT_AUTO_PUNCTUATE = "auto_punctuate";
|
field public static final java.lang.String TEXT_AUTO_PUNCTUATE = "auto_punctuate";
|
||||||
field public static final java.lang.String TEXT_AUTO_REPLACE = "auto_replace";
|
field public static final java.lang.String TEXT_AUTO_REPLACE = "auto_replace";
|
||||||
|
|||||||
@@ -35212,7 +35212,6 @@ package android.provider {
|
|||||||
field public static final java.lang.String RADIO_WIFI = "wifi";
|
field public static final java.lang.String RADIO_WIFI = "wifi";
|
||||||
field public static final java.lang.String SHOW_PROCESSES = "show_processes";
|
field public static final java.lang.String SHOW_PROCESSES = "show_processes";
|
||||||
field public static final java.lang.String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
|
field public static final java.lang.String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
|
||||||
field public static final java.lang.String SYS_PROP_SETTING_VERSION = "sys.settings_global_version";
|
|
||||||
field public static final java.lang.String THEATER_MODE_ON = "theater_mode_on";
|
field public static final java.lang.String THEATER_MODE_ON = "theater_mode_on";
|
||||||
field public static final java.lang.String TRANSITION_ANIMATION_SCALE = "transition_animation_scale";
|
field public static final java.lang.String TRANSITION_ANIMATION_SCALE = "transition_animation_scale";
|
||||||
field public static final java.lang.String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled";
|
field public static final java.lang.String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled";
|
||||||
@@ -35294,7 +35293,6 @@ package android.provider {
|
|||||||
field public static final java.lang.String SELECTED_INPUT_METHOD_SUBTYPE = "selected_input_method_subtype";
|
field public static final java.lang.String SELECTED_INPUT_METHOD_SUBTYPE = "selected_input_method_subtype";
|
||||||
field public static final java.lang.String SETTINGS_CLASSNAME = "settings_classname";
|
field public static final java.lang.String SETTINGS_CLASSNAME = "settings_classname";
|
||||||
field public static final java.lang.String SKIP_FIRST_USE_HINTS = "skip_first_use_hints";
|
field public static final java.lang.String SKIP_FIRST_USE_HINTS = "skip_first_use_hints";
|
||||||
field public static final java.lang.String SYS_PROP_SETTING_VERSION = "sys.settings_secure_version";
|
|
||||||
field public static final java.lang.String TOUCH_EXPLORATION_ENABLED = "touch_exploration_enabled";
|
field public static final java.lang.String TOUCH_EXPLORATION_ENABLED = "touch_exploration_enabled";
|
||||||
field public static final deprecated java.lang.String TTS_DEFAULT_COUNTRY = "tts_default_country";
|
field public static final deprecated java.lang.String TTS_DEFAULT_COUNTRY = "tts_default_country";
|
||||||
field public static final deprecated java.lang.String TTS_DEFAULT_LANG = "tts_default_lang";
|
field public static final deprecated java.lang.String TTS_DEFAULT_LANG = "tts_default_lang";
|
||||||
@@ -35409,7 +35407,6 @@ package android.provider {
|
|||||||
field public static final deprecated java.lang.String SHOW_WEB_SUGGESTIONS = "show_web_suggestions";
|
field public static final deprecated java.lang.String SHOW_WEB_SUGGESTIONS = "show_web_suggestions";
|
||||||
field public static final java.lang.String SOUND_EFFECTS_ENABLED = "sound_effects_enabled";
|
field public static final java.lang.String SOUND_EFFECTS_ENABLED = "sound_effects_enabled";
|
||||||
field public static final deprecated java.lang.String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
|
field public static final deprecated java.lang.String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
|
||||||
field public static final java.lang.String SYS_PROP_SETTING_VERSION = "sys.settings_system_version";
|
|
||||||
field public static final java.lang.String TEXT_AUTO_CAPS = "auto_caps";
|
field public static final java.lang.String TEXT_AUTO_CAPS = "auto_caps";
|
||||||
field public static final java.lang.String TEXT_AUTO_PUNCTUATE = "auto_punctuate";
|
field public static final java.lang.String TEXT_AUTO_PUNCTUATE = "auto_punctuate";
|
||||||
field public static final java.lang.String TEXT_AUTO_REPLACE = "auto_replace";
|
field public static final java.lang.String TEXT_AUTO_REPLACE = "auto_replace";
|
||||||
|
|||||||
@@ -32563,7 +32563,6 @@ package android.provider {
|
|||||||
field public static final java.lang.String RADIO_WIFI = "wifi";
|
field public static final java.lang.String RADIO_WIFI = "wifi";
|
||||||
field public static final java.lang.String SHOW_PROCESSES = "show_processes";
|
field public static final java.lang.String SHOW_PROCESSES = "show_processes";
|
||||||
field public static final java.lang.String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
|
field public static final java.lang.String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
|
||||||
field public static final java.lang.String SYS_PROP_SETTING_VERSION = "sys.settings_global_version";
|
|
||||||
field public static final java.lang.String TRANSITION_ANIMATION_SCALE = "transition_animation_scale";
|
field public static final java.lang.String TRANSITION_ANIMATION_SCALE = "transition_animation_scale";
|
||||||
field public static final java.lang.String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled";
|
field public static final java.lang.String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled";
|
||||||
field public static final java.lang.String USE_GOOGLE_MAIL = "use_google_mail";
|
field public static final java.lang.String USE_GOOGLE_MAIL = "use_google_mail";
|
||||||
@@ -32644,7 +32643,6 @@ package android.provider {
|
|||||||
field public static final java.lang.String SELECTED_INPUT_METHOD_SUBTYPE = "selected_input_method_subtype";
|
field public static final java.lang.String SELECTED_INPUT_METHOD_SUBTYPE = "selected_input_method_subtype";
|
||||||
field public static final java.lang.String SETTINGS_CLASSNAME = "settings_classname";
|
field public static final java.lang.String SETTINGS_CLASSNAME = "settings_classname";
|
||||||
field public static final java.lang.String SKIP_FIRST_USE_HINTS = "skip_first_use_hints";
|
field public static final java.lang.String SKIP_FIRST_USE_HINTS = "skip_first_use_hints";
|
||||||
field public static final java.lang.String SYS_PROP_SETTING_VERSION = "sys.settings_secure_version";
|
|
||||||
field public static final java.lang.String TOUCH_EXPLORATION_ENABLED = "touch_exploration_enabled";
|
field public static final java.lang.String TOUCH_EXPLORATION_ENABLED = "touch_exploration_enabled";
|
||||||
field public static final deprecated java.lang.String TTS_DEFAULT_COUNTRY = "tts_default_country";
|
field public static final deprecated java.lang.String TTS_DEFAULT_COUNTRY = "tts_default_country";
|
||||||
field public static final deprecated java.lang.String TTS_DEFAULT_LANG = "tts_default_lang";
|
field public static final deprecated java.lang.String TTS_DEFAULT_LANG = "tts_default_lang";
|
||||||
@@ -32760,7 +32758,6 @@ package android.provider {
|
|||||||
field public static final deprecated java.lang.String SHOW_WEB_SUGGESTIONS = "show_web_suggestions";
|
field public static final deprecated java.lang.String SHOW_WEB_SUGGESTIONS = "show_web_suggestions";
|
||||||
field public static final java.lang.String SOUND_EFFECTS_ENABLED = "sound_effects_enabled";
|
field public static final java.lang.String SOUND_EFFECTS_ENABLED = "sound_effects_enabled";
|
||||||
field public static final deprecated java.lang.String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
|
field public static final deprecated java.lang.String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
|
||||||
field public static final java.lang.String SYS_PROP_SETTING_VERSION = "sys.settings_system_version";
|
|
||||||
field public static final java.lang.String TEXT_AUTO_CAPS = "auto_caps";
|
field public static final java.lang.String TEXT_AUTO_CAPS = "auto_caps";
|
||||||
field public static final java.lang.String TEXT_AUTO_PUNCTUATE = "auto_punctuate";
|
field public static final java.lang.String TEXT_AUTO_PUNCTUATE = "auto_punctuate";
|
||||||
field public static final java.lang.String TEXT_AUTO_REPLACE = "auto_replace";
|
field public static final java.lang.String TEXT_AUTO_REPLACE = "auto_replace";
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package android.provider;
|
package android.provider;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
import android.annotation.SdkConstant;
|
import android.annotation.SdkConstant;
|
||||||
import android.annotation.SdkConstant.SdkConstantType;
|
import android.annotation.SdkConstant.SdkConstantType;
|
||||||
import android.annotation.SystemApi;
|
import android.annotation.SystemApi;
|
||||||
@@ -50,7 +51,6 @@ import android.os.IBinder;
|
|||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ServiceManager;
|
import android.os.ServiceManager;
|
||||||
import android.os.SystemProperties;
|
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.os.Build.VERSION_CODES;
|
import android.os.Build.VERSION_CODES;
|
||||||
import android.speech.tts.TextToSpeech;
|
import android.speech.tts.TextToSpeech;
|
||||||
@@ -61,9 +61,12 @@ import android.util.ArraySet;
|
|||||||
import android.util.LocaleList;
|
import android.util.LocaleList;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import android.util.MemoryIntArray;
|
||||||
|
import com.android.internal.annotations.GuardedBy;
|
||||||
import com.android.internal.util.ArrayUtils;
|
import com.android.internal.util.ArrayUtils;
|
||||||
import com.android.internal.widget.ILockSettings;
|
import com.android.internal.widget.ILockSettings;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -1281,6 +1284,29 @@ public final class Settings {
|
|||||||
*/
|
*/
|
||||||
public static final String CALL_METHOD_GET_GLOBAL = "GET_global";
|
public static final String CALL_METHOD_GET_GLOBAL = "GET_global";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide - Specifies that the caller of the fast-path call()-based flow tracks
|
||||||
|
* the settings generation in order to cache values locally. If this key is
|
||||||
|
* mapped to a <code>null</code> string extra in the request bundle, the response
|
||||||
|
* bundle will contain the same key mapped to a parcelable extra which would be
|
||||||
|
* an {@link android.util.MemoryIntArray}. The response will also contain an
|
||||||
|
* integer mapped to the {@link #CALL_METHOD_GENERATION_INDEX_KEY} which is the
|
||||||
|
* index in the array clients should use to lookup the generation. For efficiency
|
||||||
|
* the caller should request the generation tracking memory array only if it
|
||||||
|
* doesn't already have it.
|
||||||
|
*
|
||||||
|
* @see #CALL_METHOD_GENERATION_INDEX_KEY
|
||||||
|
*/
|
||||||
|
public static final String CALL_METHOD_TRACK_GENERATION_KEY = "_track_generation";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide Key with the location in the {@link android.util.MemoryIntArray} where
|
||||||
|
* to look up the generation id of the backing table.
|
||||||
|
*
|
||||||
|
* @see #CALL_METHOD_TRACK_GENERATION_KEY
|
||||||
|
*/
|
||||||
|
public static final String CALL_METHOD_GENERATION_INDEX_KEY = "_generation_index";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @hide - User handle argument extra to the fast-path call()-based requests
|
* @hide - User handle argument extra to the fast-path call()-based requests
|
||||||
*/
|
*/
|
||||||
@@ -1424,9 +1450,42 @@ public final class Settings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class GenerationTracker {
|
||||||
|
private final MemoryIntArray mArray;
|
||||||
|
private final int mIndex;
|
||||||
|
private int mCurrentGeneration;
|
||||||
|
|
||||||
|
public GenerationTracker(@NonNull MemoryIntArray array, int index) {
|
||||||
|
mArray = array;
|
||||||
|
mIndex = index;
|
||||||
|
mCurrentGeneration = readCurrentGeneration();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isGenerationChanged() {
|
||||||
|
final int currentGeneration = readCurrentGeneration();
|
||||||
|
if (currentGeneration >= 0) {
|
||||||
|
if (currentGeneration == mCurrentGeneration) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mCurrentGeneration = currentGeneration;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int readCurrentGeneration() {
|
||||||
|
try {
|
||||||
|
return mArray.get(mIndex);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Error getting current generation", e);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Thread-safe.
|
// Thread-safe.
|
||||||
private static class NameValueCache {
|
private static class NameValueCache {
|
||||||
private final String mVersionSystemProperty;
|
private static final boolean DEBUG = false;
|
||||||
|
|
||||||
private final Uri mUri;
|
private final Uri mUri;
|
||||||
|
|
||||||
private static final String[] SELECT_VALUE =
|
private static final String[] SELECT_VALUE =
|
||||||
@@ -1435,7 +1494,6 @@ public final class Settings {
|
|||||||
|
|
||||||
// Must synchronize on 'this' to access mValues and mValuesVersion.
|
// Must synchronize on 'this' to access mValues and mValuesVersion.
|
||||||
private final HashMap<String, String> mValues = new HashMap<String, String>();
|
private final HashMap<String, String> mValues = new HashMap<String, String>();
|
||||||
private long mValuesVersion = 0;
|
|
||||||
|
|
||||||
// Initially null; set lazily and held forever. Synchronized on 'this'.
|
// Initially null; set lazily and held forever. Synchronized on 'this'.
|
||||||
private IContentProvider mContentProvider = null;
|
private IContentProvider mContentProvider = null;
|
||||||
@@ -1445,9 +1503,10 @@ public final class Settings {
|
|||||||
private final String mCallGetCommand;
|
private final String mCallGetCommand;
|
||||||
private final String mCallSetCommand;
|
private final String mCallSetCommand;
|
||||||
|
|
||||||
public NameValueCache(String versionSystemProperty, Uri uri,
|
@GuardedBy("this")
|
||||||
String getCommand, String setCommand) {
|
private GenerationTracker mGenerationTracker;
|
||||||
mVersionSystemProperty = versionSystemProperty;
|
|
||||||
|
public NameValueCache(Uri uri, String getCommand, String setCommand) {
|
||||||
mUri = uri;
|
mUri = uri;
|
||||||
mCallGetCommand = getCommand;
|
mCallGetCommand = getCommand;
|
||||||
mCallSetCommand = setCommand;
|
mCallSetCommand = setCommand;
|
||||||
@@ -1482,22 +1541,18 @@ public final class Settings {
|
|||||||
public String getStringForUser(ContentResolver cr, String name, final int userHandle) {
|
public String getStringForUser(ContentResolver cr, String name, final int userHandle) {
|
||||||
final boolean isSelf = (userHandle == UserHandle.myUserId());
|
final boolean isSelf = (userHandle == UserHandle.myUserId());
|
||||||
if (isSelf) {
|
if (isSelf) {
|
||||||
long newValuesVersion = SystemProperties.getLong(mVersionSystemProperty, 0);
|
|
||||||
|
|
||||||
// Our own user's settings data uses a client-side cache
|
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (mValuesVersion != newValuesVersion) {
|
if (mGenerationTracker != null) {
|
||||||
if (LOCAL_LOGV || false) {
|
if (mGenerationTracker.isGenerationChanged()) {
|
||||||
Log.v(TAG, "invalidate [" + mUri.getLastPathSegment() + "]: current "
|
if (DEBUG) {
|
||||||
+ newValuesVersion + " != cached " + mValuesVersion);
|
Log.i(TAG, "Generation changed for type:"
|
||||||
|
+ mUri.getPath() + " in package:"
|
||||||
|
+ cr.getPackageName() +" and user:" + userHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
mValues.clear();
|
mValues.clear();
|
||||||
mValuesVersion = newValuesVersion;
|
} else if (mValues.containsKey(name)) {
|
||||||
|
return mValues.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mValues.containsKey(name)) {
|
|
||||||
return mValues.get(name); // Could be null, that's OK -- negative caching
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1518,12 +1573,42 @@ public final class Settings {
|
|||||||
args = new Bundle();
|
args = new Bundle();
|
||||||
args.putInt(CALL_METHOD_USER_KEY, userHandle);
|
args.putInt(CALL_METHOD_USER_KEY, userHandle);
|
||||||
}
|
}
|
||||||
|
boolean needsGenerationTracker = false;
|
||||||
|
synchronized (this) {
|
||||||
|
if (isSelf && mGenerationTracker == null) {
|
||||||
|
needsGenerationTracker = true;
|
||||||
|
if (args == null) {
|
||||||
|
args = new Bundle();
|
||||||
|
}
|
||||||
|
args.putString(CALL_METHOD_TRACK_GENERATION_KEY, null);
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.i(TAG, "Requested generation tracker for type: "+ mUri.getPath()
|
||||||
|
+ " in package:" + cr.getPackageName() +" and user:"
|
||||||
|
+ userHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Bundle b = cp.call(cr.getPackageName(), mCallGetCommand, name, args);
|
Bundle b = cp.call(cr.getPackageName(), mCallGetCommand, name, args);
|
||||||
if (b != null) {
|
if (b != null) {
|
||||||
String value = b.getPairValue();
|
String value = b.getString(Settings.NameValueTable.VALUE);
|
||||||
// Don't update our cache for reads of other users' data
|
// Don't update our cache for reads of other users' data
|
||||||
if (isSelf) {
|
if (isSelf) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
|
if (needsGenerationTracker) {
|
||||||
|
MemoryIntArray array = b.getParcelable(
|
||||||
|
CALL_METHOD_TRACK_GENERATION_KEY);
|
||||||
|
final int index = b.getInt(
|
||||||
|
CALL_METHOD_GENERATION_INDEX_KEY, -1);
|
||||||
|
if (array != null && index >= 0) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.i(TAG, "Received generation tracker for type:"
|
||||||
|
+ mUri.getPath() + " in package:"
|
||||||
|
+ cr.getPackageName() + " and user:"
|
||||||
|
+ userHandle + " with index:" + index);
|
||||||
|
}
|
||||||
|
mGenerationTracker = new GenerationTracker(array, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
mValues.put(name, value);
|
mValues.put(name, value);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1592,8 +1677,6 @@ public final class Settings {
|
|||||||
* functions for accessing individual settings entries.
|
* functions for accessing individual settings entries.
|
||||||
*/
|
*/
|
||||||
public static final class System extends NameValueTable {
|
public static final class System extends NameValueTable {
|
||||||
public static final String SYS_PROP_SETTING_VERSION = "sys.settings_system_version";
|
|
||||||
|
|
||||||
private static final float DEFAULT_FONT_SCALE = 1.0f;
|
private static final float DEFAULT_FONT_SCALE = 1.0f;
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
@@ -1608,7 +1691,6 @@ public final class Settings {
|
|||||||
Uri.parse("content://" + AUTHORITY + "/system");
|
Uri.parse("content://" + AUTHORITY + "/system");
|
||||||
|
|
||||||
private static final NameValueCache sNameValueCache = new NameValueCache(
|
private static final NameValueCache sNameValueCache = new NameValueCache(
|
||||||
SYS_PROP_SETTING_VERSION,
|
|
||||||
CONTENT_URI,
|
CONTENT_URI,
|
||||||
CALL_METHOD_GET_SYSTEM,
|
CALL_METHOD_GET_SYSTEM,
|
||||||
CALL_METHOD_PUT_SYSTEM);
|
CALL_METHOD_PUT_SYSTEM);
|
||||||
@@ -3913,8 +3995,6 @@ public final class Settings {
|
|||||||
* APIs for those values, not modified directly by applications.
|
* APIs for those values, not modified directly by applications.
|
||||||
*/
|
*/
|
||||||
public static final class Secure extends NameValueTable {
|
public static final class Secure extends NameValueTable {
|
||||||
public static final String SYS_PROP_SETTING_VERSION = "sys.settings_secure_version";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The content:// style URL for this table
|
* The content:// style URL for this table
|
||||||
*/
|
*/
|
||||||
@@ -3923,7 +4003,6 @@ public final class Settings {
|
|||||||
|
|
||||||
// Populated lazily, guarded by class object:
|
// Populated lazily, guarded by class object:
|
||||||
private static final NameValueCache sNameValueCache = new NameValueCache(
|
private static final NameValueCache sNameValueCache = new NameValueCache(
|
||||||
SYS_PROP_SETTING_VERSION,
|
|
||||||
CONTENT_URI,
|
CONTENT_URI,
|
||||||
CALL_METHOD_GET_SECURE,
|
CALL_METHOD_GET_SECURE,
|
||||||
CALL_METHOD_PUT_SECURE);
|
CALL_METHOD_PUT_SECURE);
|
||||||
@@ -6360,8 +6439,6 @@ public final class Settings {
|
|||||||
* explicitly modify through the system UI or specialized APIs for those values.
|
* explicitly modify through the system UI or specialized APIs for those values.
|
||||||
*/
|
*/
|
||||||
public static final class Global extends NameValueTable {
|
public static final class Global extends NameValueTable {
|
||||||
public static final String SYS_PROP_SETTING_VERSION = "sys.settings_global_version";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The content:// style URL for global secure settings items. Not public.
|
* The content:// style URL for global secure settings items. Not public.
|
||||||
*/
|
*/
|
||||||
@@ -8412,7 +8489,6 @@ public final class Settings {
|
|||||||
|
|
||||||
// Populated lazily, guarded by class object:
|
// Populated lazily, guarded by class object:
|
||||||
private static NameValueCache sNameValueCache = new NameValueCache(
|
private static NameValueCache sNameValueCache = new NameValueCache(
|
||||||
SYS_PROP_SETTING_VERSION,
|
|
||||||
CONTENT_URI,
|
CONTENT_URI,
|
||||||
CALL_METHOD_GET_GLOBAL,
|
CALL_METHOD_GET_GLOBAL,
|
||||||
CALL_METHOD_PUT_GLOBAL);
|
CALL_METHOD_PUT_GLOBAL);
|
||||||
|
|||||||
19
core/java/android/util/MemoryIntArray.aidl
Normal file
19
core/java/android/util/MemoryIntArray.aidl
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2016, The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.util;
|
||||||
|
|
||||||
|
parcelable MemoryIntArray;
|
||||||
258
core/java/android/util/MemoryIntArray.java
Normal file
258
core/java/android/util/MemoryIntArray.java
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.util;
|
||||||
|
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
import android.os.Process;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is an array of integers that is backed by shared memory.
|
||||||
|
* It is useful for efficiently sharing state between processes. The
|
||||||
|
* write and read operations are guaranteed to not result in read/
|
||||||
|
* write memory tear, i.e. they are atomic. However, multiple read/
|
||||||
|
* write operations are <strong>not</strong> synchronized between
|
||||||
|
* each other.
|
||||||
|
* <p>
|
||||||
|
* The data structure is designed to have one owner process that can
|
||||||
|
* read/write. There may be multiple client processes that can only read or
|
||||||
|
* read/write depending how the data structure was configured when
|
||||||
|
* instantiated. The owner process is the process that created the array.
|
||||||
|
* The shared memory is pinned (not reclaimed by the system) until the
|
||||||
|
* owning process dies or the data structure is closed. This class
|
||||||
|
* is <strong>not</strong> thread safe. You should not interact with
|
||||||
|
* an instance of this class once it is closed.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public final class MemoryIntArray implements Parcelable, Closeable {
|
||||||
|
private static final int MAX_SIZE = 1024;
|
||||||
|
|
||||||
|
private final int mOwnerPid;
|
||||||
|
private final boolean mClientWritable;
|
||||||
|
private final long mMemoryAddr;
|
||||||
|
private ParcelFileDescriptor mFd;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance.
|
||||||
|
*
|
||||||
|
* @param size The size of the array in terms of integer slots. Cannot be
|
||||||
|
* more than {@link #getMaxSize()}.
|
||||||
|
* @param clientWritable Whether other processes can write to the array.
|
||||||
|
* @throws IOException If an error occurs while accessing the shared memory.
|
||||||
|
*/
|
||||||
|
public MemoryIntArray(int size, boolean clientWritable) throws IOException {
|
||||||
|
if (size > MAX_SIZE) {
|
||||||
|
throw new IllegalArgumentException("Max size is " + MAX_SIZE);
|
||||||
|
}
|
||||||
|
mOwnerPid = Process.myPid();
|
||||||
|
mClientWritable = clientWritable;
|
||||||
|
final String name = UUID.randomUUID().toString();
|
||||||
|
mFd = ParcelFileDescriptor.fromFd(nativeCreate(name, size));
|
||||||
|
mMemoryAddr = nativeOpen(mFd.getFd(), true, clientWritable);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MemoryIntArray(Parcel parcel) throws IOException {
|
||||||
|
mOwnerPid = parcel.readInt();
|
||||||
|
mClientWritable = (parcel.readInt() == 1);
|
||||||
|
mFd = parcel.readParcelable(null);
|
||||||
|
if (mFd == null) {
|
||||||
|
throw new IOException("No backing file descriptor");
|
||||||
|
}
|
||||||
|
final long memoryAddress = parcel.readLong();
|
||||||
|
if (isOwner()) {
|
||||||
|
mMemoryAddr = memoryAddress;
|
||||||
|
} else {
|
||||||
|
mMemoryAddr = nativeOpen(mFd.getFd(), false, mClientWritable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Gets whether this array is mutable.
|
||||||
|
*/
|
||||||
|
public boolean isWritable() {
|
||||||
|
enforceNotClosed();
|
||||||
|
return isOwner() || mClientWritable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value at a given index.
|
||||||
|
*
|
||||||
|
* @param index The index.
|
||||||
|
* @return The value at this index.
|
||||||
|
* @throws IOException If an error occurs while accessing the shared memory.
|
||||||
|
*/
|
||||||
|
public int get(int index) throws IOException {
|
||||||
|
enforceNotClosed();
|
||||||
|
enforceValidIndex(index);
|
||||||
|
return nativeGet(mFd.getFd(), mMemoryAddr, index, isOwner());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value at a given index. This method can be called only if
|
||||||
|
* {@link #isWritable()} returns true which means your process is the
|
||||||
|
* owner.
|
||||||
|
*
|
||||||
|
* @param index The index.
|
||||||
|
* @param value The value to set.
|
||||||
|
* @throws IOException If an error occurs while accessing the shared memory.
|
||||||
|
*/
|
||||||
|
public void set(int index, int value) throws IOException {
|
||||||
|
enforceNotClosed();
|
||||||
|
enforceWritable();
|
||||||
|
enforceValidIndex(index);
|
||||||
|
nativeSet(mFd.getFd(), mMemoryAddr, index, value, isOwner());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the array size.
|
||||||
|
*
|
||||||
|
* @throws IOException If an error occurs while accessing the shared memory.
|
||||||
|
*/
|
||||||
|
public int size() throws IOException {
|
||||||
|
enforceNotClosed();
|
||||||
|
return nativeSize(mFd.getFd());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the array releasing resources.
|
||||||
|
*
|
||||||
|
* @throws IOException If an error occurs while accessing the shared memory.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
if (!isClosed()) {
|
||||||
|
nativeClose(mFd.getFd(), mMemoryAddr, isOwner());
|
||||||
|
mFd = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Whether this array is closed and shouldn't be used.
|
||||||
|
*/
|
||||||
|
public boolean isClosed() {
|
||||||
|
return mFd == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void finalize() throws Throwable {
|
||||||
|
close();
|
||||||
|
super.finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return CONTENTS_FILE_DESCRIPTOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel parcel, int flags) {
|
||||||
|
parcel.writeInt(mOwnerPid);
|
||||||
|
parcel.writeInt(mClientWritable ? 1 : 0);
|
||||||
|
parcel.writeParcelable(mFd, 0);
|
||||||
|
parcel.writeLong(mMemoryAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
MemoryIntArray other = (MemoryIntArray) obj;
|
||||||
|
if (mFd == null) {
|
||||||
|
if (other.mFd != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (mFd.getFd() != other.mFd.getFd()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return mFd != null ? mFd.hashCode() : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isOwner() {
|
||||||
|
return mOwnerPid == Process.myPid();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enforceNotClosed() {
|
||||||
|
if (isClosed()) {
|
||||||
|
throw new IllegalStateException("cannot interact with a closed instance");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enforceValidIndex(int index) throws IOException {
|
||||||
|
final int size = size();
|
||||||
|
if (index < 0 || index > size - 1) {
|
||||||
|
throw new IndexOutOfBoundsException(
|
||||||
|
index + " not between 0 and " + (size - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enforceWritable() {
|
||||||
|
if (!isWritable()) {
|
||||||
|
throw new UnsupportedOperationException("array is not writable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private native int nativeCreate(String name, int size);
|
||||||
|
private native long nativeOpen(int fd, boolean owner, boolean writable);
|
||||||
|
private native void nativeClose(int fd, long memoryAddr, boolean owner);
|
||||||
|
private native int nativeGet(int fd, long memoryAddr, int index, boolean owner);
|
||||||
|
private native void nativeSet(int fd, long memoryAddr, int index, int value, boolean owner);
|
||||||
|
private native int nativeSize(int fd);
|
||||||
|
private native static int nativeGetMemoryPageSize();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The max array size.
|
||||||
|
*/
|
||||||
|
public static int getMaxSize() {
|
||||||
|
return MAX_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Parcelable.Creator<MemoryIntArray> CREATOR =
|
||||||
|
new Parcelable.Creator<MemoryIntArray>() {
|
||||||
|
@Override
|
||||||
|
public MemoryIntArray createFromParcel(Parcel parcel) {
|
||||||
|
try {
|
||||||
|
return new MemoryIntArray(parcel);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
throw new RuntimeException(ioe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MemoryIntArray[] newArray(int size) {
|
||||||
|
return new MemoryIntArray[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -95,6 +95,7 @@ LOCAL_SRC_FILES:= \
|
|||||||
android_util_AssetManager.cpp \
|
android_util_AssetManager.cpp \
|
||||||
android_util_Binder.cpp \
|
android_util_Binder.cpp \
|
||||||
android_util_EventLog.cpp \
|
android_util_EventLog.cpp \
|
||||||
|
android_util_MemoryIntArray.cpp \
|
||||||
android_util_Log.cpp \
|
android_util_Log.cpp \
|
||||||
android_util_PathParser.cpp \
|
android_util_PathParser.cpp \
|
||||||
android_util_Process.cpp \
|
android_util_Process.cpp \
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ extern int register_android_app_admin_SecurityLog(JNIEnv* env);
|
|||||||
extern int register_android_content_AssetManager(JNIEnv* env);
|
extern int register_android_content_AssetManager(JNIEnv* env);
|
||||||
extern int register_android_util_EventLog(JNIEnv* env);
|
extern int register_android_util_EventLog(JNIEnv* env);
|
||||||
extern int register_android_util_Log(JNIEnv* env);
|
extern int register_android_util_Log(JNIEnv* env);
|
||||||
|
extern int register_android_util_MemoryIntArray(JNIEnv* env);
|
||||||
extern int register_android_util_PathParser(JNIEnv* env);
|
extern int register_android_util_PathParser(JNIEnv* env);
|
||||||
extern int register_android_content_StringBlock(JNIEnv* env);
|
extern int register_android_content_StringBlock(JNIEnv* env);
|
||||||
extern int register_android_content_XmlBlock(JNIEnv* env);
|
extern int register_android_content_XmlBlock(JNIEnv* env);
|
||||||
@@ -1256,6 +1257,7 @@ static const RegJNIRec gRegJNI[] = {
|
|||||||
REG_JNI(register_android_os_SystemClock),
|
REG_JNI(register_android_os_SystemClock),
|
||||||
REG_JNI(register_android_util_EventLog),
|
REG_JNI(register_android_util_EventLog),
|
||||||
REG_JNI(register_android_util_Log),
|
REG_JNI(register_android_util_Log),
|
||||||
|
REG_JNI(register_android_util_MemoryIntArray),
|
||||||
REG_JNI(register_android_util_PathParser),
|
REG_JNI(register_android_util_PathParser),
|
||||||
REG_JNI(register_android_app_admin_SecurityLog),
|
REG_JNI(register_android_app_admin_SecurityLog),
|
||||||
REG_JNI(register_android_content_AssetManager),
|
REG_JNI(register_android_content_AssetManager),
|
||||||
|
|||||||
219
core/jni/android_util_MemoryIntArray.cpp
Normal file
219
core/jni/android_util_MemoryIntArray.cpp
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "core_jni_helpers.h"
|
||||||
|
#include <cutils/ashmem.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
static jint android_util_MemoryIntArray_create(JNIEnv* env, jobject clazz, jstring name,
|
||||||
|
jint size)
|
||||||
|
{
|
||||||
|
if (name == NULL) {
|
||||||
|
jniThrowException(env, "java/io/IOException", "bad name");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size <= 0) {
|
||||||
|
jniThrowException(env, "java/io/IOException", "bad size");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* nameStr = env->GetStringUTFChars(name, NULL);
|
||||||
|
const int ashmemSize = sizeof(std::atomic_int) * size;
|
||||||
|
int fd = ashmem_create_region(nameStr, ashmemSize);
|
||||||
|
env->ReleaseStringUTFChars(name, nameStr);
|
||||||
|
|
||||||
|
if (fd < 0) {
|
||||||
|
jniThrowException(env, "java/io/IOException", "ashmem creation failed");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) {
|
||||||
|
jniThrowException(env, "java/io/IOException", "ashmem was purged");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int setProtResult = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE);
|
||||||
|
if (setProtResult < 0) {
|
||||||
|
jniThrowException(env, "java/io/IOException", "cannot set ashmem prot mode");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static jlong android_util_MemoryIntArray_open(JNIEnv* env, jobject clazz, jint fd,
|
||||||
|
jboolean owner, jboolean writable)
|
||||||
|
{
|
||||||
|
if (fd < 0) {
|
||||||
|
jniThrowException(env, "java/io/IOException", "bad file descriptor");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ashmemSize = ashmem_get_size_region(fd);
|
||||||
|
if (ashmemSize <= 0) {
|
||||||
|
jniThrowException(env, "java/io/IOException", "bad ashmem size");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int protMode = (owner || writable) ? (PROT_READ | PROT_WRITE) : PROT_READ;
|
||||||
|
void* ashmemAddr = mmap(NULL, ashmemSize, protMode, MAP_SHARED, fd, 0);
|
||||||
|
if (ashmemAddr == MAP_FAILED) {
|
||||||
|
jniThrowException(env, "java/io/IOException", "cannot mmap ashmem");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (owner) {
|
||||||
|
int size = ashmemSize / sizeof(std::atomic_int);
|
||||||
|
new (ashmemAddr) std::atomic_int[size];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (owner && !writable) {
|
||||||
|
int setProtResult = ashmem_set_prot_region(fd, PROT_READ);
|
||||||
|
if (setProtResult < 0) {
|
||||||
|
jniThrowException(env, "java/io/IOException", "cannot set ashmem prot mode");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return reinterpret_cast<jlong>(ashmemAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void android_util_MemoryIntArray_close(JNIEnv* env, jobject clazz, jint fd,
|
||||||
|
jlong ashmemAddr, jboolean owner)
|
||||||
|
{
|
||||||
|
if (fd < 0) {
|
||||||
|
jniThrowException(env, "java/io/IOException", "bad file descriptor");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ashmemSize = ashmem_get_size_region(fd);
|
||||||
|
if (ashmemSize <= 0) {
|
||||||
|
jniThrowException(env, "java/io/IOException", "bad ashmem size");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int unmapResult = munmap(reinterpret_cast<void *>(ashmemAddr), ashmemSize);
|
||||||
|
if (unmapResult < 0) {
|
||||||
|
jniThrowException(env, "java/io/IOException", "munmap failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't deallocate the atomic ints we created with placement new in the ashmem
|
||||||
|
// region as the kernel we reclaim all pages when the ashmem region is destroyed.
|
||||||
|
if (owner && (ashmem_unpin_region(fd, 0, 0) != ASHMEM_IS_UNPINNED)) {
|
||||||
|
jniThrowException(env, "java/io/IOException", "ashmem unpinning failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static jint android_util_MemoryIntArray_get(JNIEnv* env, jobject clazz,
|
||||||
|
jint fd, jlong address, jint index, jboolean owner)
|
||||||
|
{
|
||||||
|
if (fd < 0) {
|
||||||
|
jniThrowException(env, "java/io/IOException", "bad file descriptor");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool unpin = false;
|
||||||
|
|
||||||
|
if (!owner) {
|
||||||
|
if (ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) {
|
||||||
|
jniThrowException(env, "java/io/IOException", "ashmem region was purged");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
unpin = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::atomic_int* value = reinterpret_cast<std::atomic_int*>(address) + index;
|
||||||
|
const int result = value->load(std::memory_order_relaxed);
|
||||||
|
|
||||||
|
if (unpin) {
|
||||||
|
ashmem_unpin_region(fd, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void android_util_MemoryIntArray_set(JNIEnv* env, jobject clazz,
|
||||||
|
jint fd, jlong address, jint index, jint newValue, jboolean owner)
|
||||||
|
{
|
||||||
|
if (fd < 0) {
|
||||||
|
jniThrowException(env, "java/io/IOException", "bad file descriptor");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool unpin = false;
|
||||||
|
|
||||||
|
if (!owner) {
|
||||||
|
if (ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) {
|
||||||
|
jniThrowException(env, "java/io/IOException", "ashmem region was purged");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unpin = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::atomic_int* value = reinterpret_cast<std::atomic_int*>(address) + index;
|
||||||
|
value->store(newValue, std::memory_order_relaxed);
|
||||||
|
|
||||||
|
if (unpin) {
|
||||||
|
ashmem_unpin_region(fd, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static jint android_util_MemoryIntArray_size(JNIEnv* env, jobject clazz, jint fd) {
|
||||||
|
if (fd < 0) {
|
||||||
|
jniThrowException(env, "java/io/IOException", "bad file descriptor");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use ASHMEM_GET_SIZE to find out if the fd refers to an ashmem region.
|
||||||
|
// ASHMEM_GET_SIZE should succeed for all ashmem regions, and the kernel
|
||||||
|
// should return ENOTTY for all other valid file descriptors
|
||||||
|
int ashmemSize = ashmem_get_size_region(fd);
|
||||||
|
if (ashmemSize < 0) {
|
||||||
|
if (errno == ENOTTY) {
|
||||||
|
// ENOTTY means that the ioctl does not apply to this object,
|
||||||
|
// i.e., it is not an ashmem region.
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// Some other error, throw exception
|
||||||
|
jniThrowIOException(env, errno);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return ashmemSize / sizeof(std::atomic_int);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const JNINativeMethod methods[] = {
|
||||||
|
{"nativeCreate", "(Ljava/lang/String;I)I", (void*)android_util_MemoryIntArray_create},
|
||||||
|
{"nativeOpen", "(IZZ)J", (void*)android_util_MemoryIntArray_open},
|
||||||
|
{"nativeClose", "(IJZ)V", (void*)android_util_MemoryIntArray_close},
|
||||||
|
{"nativeGet", "(IJIZ)I", (void*)android_util_MemoryIntArray_get},
|
||||||
|
{"nativeSet", "(IJIIZ)V", (void*) android_util_MemoryIntArray_set},
|
||||||
|
{"nativeSize", "(I)I", (void*) android_util_MemoryIntArray_size},
|
||||||
|
};
|
||||||
|
|
||||||
|
int register_android_util_MemoryIntArray(JNIEnv* env)
|
||||||
|
{
|
||||||
|
return RegisterMethodsOrDie(env, "android/util/MemoryIntArray", methods, NELEM(methods));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
192
core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
Normal file
192
core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.util;
|
||||||
|
|
||||||
|
import android.os.Parcel;
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
import libcore.io.IoUtils;
|
||||||
|
|
||||||
|
public class MemoryIntArrayTest extends TestCase {
|
||||||
|
|
||||||
|
public void testSize() throws Exception {
|
||||||
|
MemoryIntArray array = null;
|
||||||
|
try {
|
||||||
|
array = new MemoryIntArray(3, false);
|
||||||
|
assertEquals("size must be three", 3, array.size());
|
||||||
|
} finally {
|
||||||
|
IoUtils.closeQuietly(array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGetSet() throws Exception {
|
||||||
|
MemoryIntArray array = null;
|
||||||
|
try {
|
||||||
|
array = new MemoryIntArray(3, false);
|
||||||
|
|
||||||
|
array.set(0, 1);
|
||||||
|
array.set(1, 2);
|
||||||
|
array.set(2, 3);
|
||||||
|
|
||||||
|
assertEquals("First element should be 1", 1, array.get(0));
|
||||||
|
assertEquals("First element should be 2", 2, array.get(1));
|
||||||
|
assertEquals("First element should be 3", 3, array.get(2));
|
||||||
|
} finally {
|
||||||
|
IoUtils.closeQuietly(array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testWritable() throws Exception {
|
||||||
|
MemoryIntArray array = null;
|
||||||
|
try {
|
||||||
|
array = new MemoryIntArray(3, true);
|
||||||
|
assertTrue("Must be mutable", array.isWritable());
|
||||||
|
} finally {
|
||||||
|
IoUtils.closeQuietly(array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testClose() throws Exception {
|
||||||
|
MemoryIntArray array = null;
|
||||||
|
try {
|
||||||
|
array = new MemoryIntArray(3, false);
|
||||||
|
array.close();
|
||||||
|
assertTrue("Must be closed", array.isClosed());
|
||||||
|
} finally {
|
||||||
|
if (array != null && !array.isClosed()) {
|
||||||
|
IoUtils.closeQuietly(array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMarshalledGetSet() throws Exception {
|
||||||
|
MemoryIntArray firstArray = null;
|
||||||
|
MemoryIntArray secondArray = null;
|
||||||
|
try {
|
||||||
|
firstArray = new MemoryIntArray(3, false);
|
||||||
|
|
||||||
|
firstArray.set(0, 1);
|
||||||
|
firstArray.set(1, 2);
|
||||||
|
firstArray.set(2, 3);
|
||||||
|
|
||||||
|
Parcel parcel = Parcel.obtain();
|
||||||
|
parcel.writeParcelable(firstArray, 0);
|
||||||
|
parcel.setDataPosition(0);
|
||||||
|
secondArray = parcel.readParcelable(null);
|
||||||
|
parcel.recycle();
|
||||||
|
|
||||||
|
assertNotNull("Should marshall file descriptor", secondArray);
|
||||||
|
|
||||||
|
assertEquals("First element should be 1", 1, secondArray.get(0));
|
||||||
|
assertEquals("First element should be 2", 2, secondArray.get(1));
|
||||||
|
assertEquals("First element should be 3", 3, secondArray.get(2));
|
||||||
|
} finally {
|
||||||
|
IoUtils.closeQuietly(firstArray);
|
||||||
|
IoUtils.closeQuietly(secondArray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testInteractOnceClosed() throws Exception {
|
||||||
|
MemoryIntArray array = null;
|
||||||
|
try {
|
||||||
|
array = new MemoryIntArray(3, false);
|
||||||
|
array.close();
|
||||||
|
|
||||||
|
array.close();
|
||||||
|
|
||||||
|
try {
|
||||||
|
array.size();
|
||||||
|
fail("Cannot interact with a closed instance");
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
/* expected */
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
array.get(0);
|
||||||
|
fail("Cannot interact with a closed instance");
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
/* expected */
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
array.set(0, 1);
|
||||||
|
fail("Cannot interact with a closed instance");
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
/* expected */
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
array.isWritable();
|
||||||
|
fail("Cannot interact with a closed instance");
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
/* expected */
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (array != null && !array.isClosed()) {
|
||||||
|
IoUtils.closeQuietly(array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testInteractPutOfBounds() throws Exception {
|
||||||
|
MemoryIntArray array = null;
|
||||||
|
try {
|
||||||
|
array = new MemoryIntArray(3, false);
|
||||||
|
|
||||||
|
try {
|
||||||
|
array.get(-1);
|
||||||
|
fail("Cannot interact out of array bounds");
|
||||||
|
} catch (IndexOutOfBoundsException e) {
|
||||||
|
/* expected */
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
array.get(3);
|
||||||
|
fail("Cannot interact out of array bounds");
|
||||||
|
} catch (IndexOutOfBoundsException e) {
|
||||||
|
/* expected */
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
array.set(-1, 0);
|
||||||
|
fail("Cannot interact out of array bounds");
|
||||||
|
} catch (IndexOutOfBoundsException e) {
|
||||||
|
/* expected */
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
array.set(3, 0);
|
||||||
|
fail("Cannot interact out of array bounds");
|
||||||
|
} catch (IndexOutOfBoundsException e) {
|
||||||
|
/* expected */
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
IoUtils.closeQuietly(array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testOverMaxSize() throws Exception {
|
||||||
|
MemoryIntArray array = null;
|
||||||
|
try {
|
||||||
|
array = new MemoryIntArray(MemoryIntArray.getMaxSize() + 1, false);
|
||||||
|
fail("Cannot use over max size");
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
/* expected */
|
||||||
|
} finally {
|
||||||
|
IoUtils.closeQuietly(array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.providers.settings;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.UserManager;
|
||||||
|
import android.provider.Settings;
|
||||||
|
import android.util.MemoryIntArray;
|
||||||
|
import android.util.Slog;
|
||||||
|
import android.util.SparseIntArray;
|
||||||
|
import com.android.internal.annotations.GuardedBy;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class tracks changes for global/secure/system tables on a
|
||||||
|
* per user basis and updates a shared memory region which client
|
||||||
|
* processes can read to determine if their local caches are stale,
|
||||||
|
*/
|
||||||
|
final class GenerationRegistry {
|
||||||
|
private static final String LOG_TAG = "GenerationTracker";
|
||||||
|
|
||||||
|
private static final boolean DEBUG = false;
|
||||||
|
|
||||||
|
private final Object mLock;
|
||||||
|
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
private final SparseIntArray mKeyToIndexMap = new SparseIntArray();
|
||||||
|
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
private final MemoryIntArray mImpl;
|
||||||
|
|
||||||
|
public GenerationRegistry(Object lock) {
|
||||||
|
mLock = lock;
|
||||||
|
// One for the global table, two for system and secure tables for a
|
||||||
|
// managed profile (managed profile is not included in the max user
|
||||||
|
// count), ten for partially deleted users if users are quickly removed,
|
||||||
|
// and twice max user count for system and secure.
|
||||||
|
final int size = 1 + 2 + 10 + 2 * UserManager.getMaxSupportedUsers();
|
||||||
|
MemoryIntArray impl = null;
|
||||||
|
try {
|
||||||
|
impl = new MemoryIntArray(size, false);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Slog.e(LOG_TAG, "Error creating generation tracker", e);
|
||||||
|
}
|
||||||
|
mImpl = impl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void incrementGeneration(int key) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
if (mImpl != null) {
|
||||||
|
try {
|
||||||
|
final int index = getKeyIndexLocked(key);
|
||||||
|
if (index >= 0) {
|
||||||
|
final int generation = mImpl.get(index) + 1;
|
||||||
|
mImpl.set(index, generation);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
Slog.e(LOG_TAG, "Error updating generation id", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addGenerationData(Bundle bundle, int key) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
if (mImpl != null) {
|
||||||
|
final int index = getKeyIndexLocked(key);
|
||||||
|
if (index >= 0) {
|
||||||
|
bundle.putParcelable(Settings.CALL_METHOD_TRACK_GENERATION_KEY, mImpl);
|
||||||
|
bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, index);
|
||||||
|
if (DEBUG) {
|
||||||
|
Slog.i(LOG_TAG, "Exported index:" + index + " for key:"
|
||||||
|
+ SettingsProvider.keyToString(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getKeyIndexLocked(int key) {
|
||||||
|
int index = mKeyToIndexMap.get(key, -1);
|
||||||
|
if (index < 0) {
|
||||||
|
index = findNextEmptyIndex();
|
||||||
|
if (index >= 0) {
|
||||||
|
try {
|
||||||
|
mImpl.set(index, 1);
|
||||||
|
mKeyToIndexMap.append(key, index);
|
||||||
|
if (DEBUG) {
|
||||||
|
Slog.i(LOG_TAG, "Allocated index:" + index + " for key:"
|
||||||
|
+ SettingsProvider.keyToString(key));
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
Slog.e(LOG_TAG, "Cannot write to generation memory array", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Slog.e(LOG_TAG, "Could not allocate generation index");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onUserRemoved(int userId) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
if (mImpl != null && mKeyToIndexMap.size() > 0) {
|
||||||
|
final int secureKey = SettingsProvider.makeKey(
|
||||||
|
SettingsProvider.SETTINGS_TYPE_SECURE, userId);
|
||||||
|
resetSlotForKeyLocked(secureKey);
|
||||||
|
|
||||||
|
final int systemKey = SettingsProvider.makeKey(
|
||||||
|
SettingsProvider.SETTINGS_TYPE_SYSTEM, userId);
|
||||||
|
resetSlotForKeyLocked(systemKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetSlotForKeyLocked(int key) {
|
||||||
|
final int index = mKeyToIndexMap.get(key, -1);
|
||||||
|
if (index >= 0) {
|
||||||
|
mKeyToIndexMap.delete(key);
|
||||||
|
try {
|
||||||
|
mImpl.set(index, 0);
|
||||||
|
if (DEBUG) {
|
||||||
|
Slog.i(LOG_TAG, "Freed index:" + index + " for key:"
|
||||||
|
+ SettingsProvider.keyToString(key));
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
Slog.e(LOG_TAG, "Cannot write to generation memory array", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int findNextEmptyIndex() {
|
||||||
|
try {
|
||||||
|
final int size = mImpl.size();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
if (mImpl.get(i) == 0) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
Slog.e(LOG_TAG, "Error reading generation memory array", e);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -42,6 +42,7 @@ import android.net.Uri;
|
|||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Debug;
|
||||||
import android.os.DropBoxManager;
|
import android.os.DropBoxManager;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@@ -51,9 +52,9 @@ import android.os.ParcelFileDescriptor;
|
|||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.SELinux;
|
import android.os.SELinux;
|
||||||
import android.os.SystemProperties;
|
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
|
import android.os.UserManagerInternal;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
@@ -64,6 +65,7 @@ import com.android.internal.annotations.GuardedBy;
|
|||||||
import com.android.internal.content.PackageMonitor;
|
import com.android.internal.content.PackageMonitor;
|
||||||
import com.android.internal.os.BackgroundThread;
|
import com.android.internal.os.BackgroundThread;
|
||||||
import com.android.providers.settings.SettingsState.Setting;
|
import com.android.providers.settings.SettingsState.Setting;
|
||||||
|
import com.android.server.LocalServices;
|
||||||
import com.android.server.SystemConfig;
|
import com.android.server.SystemConfig;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -146,8 +148,15 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
Settings.NameValueTable.VALUE
|
Settings.NameValueTable.VALUE
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final Bundle NULL_SETTING = Bundle.forPair(Settings.NameValueTable.VALUE, null);
|
public static final int SETTINGS_TYPE_GLOBAL = 0;
|
||||||
|
public static final int SETTINGS_TYPE_SYSTEM = 1;
|
||||||
|
public static final int SETTINGS_TYPE_SECURE = 2;
|
||||||
|
|
||||||
|
public static final int SETTINGS_TYPE_MASK = 0xF0000000;
|
||||||
|
public static final int SETTINGS_TYPE_SHIFT = 28;
|
||||||
|
|
||||||
|
private static final Bundle NULL_SETTING_BUNDLE = Bundle.forPair(
|
||||||
|
Settings.NameValueTable.VALUE, null);
|
||||||
|
|
||||||
// Per user secure settings that moved to the for all users global settings.
|
// Per user secure settings that moved to the for all users global settings.
|
||||||
static final Set<String> sSecureMovedToGlobalSettings = new ArraySet<>();
|
static final Set<String> sSecureMovedToGlobalSettings = new ArraySet<>();
|
||||||
@@ -196,6 +205,40 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
// We have to call in the package manager with no lock held,
|
// We have to call in the package manager with no lock held,
|
||||||
private volatile IPackageManager mPackageManager;
|
private volatile IPackageManager mPackageManager;
|
||||||
|
|
||||||
|
public static int makeKey(int type, int userId) {
|
||||||
|
return (type << SETTINGS_TYPE_SHIFT) | userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getTypeFromKey(int key) {
|
||||||
|
return key >>> SETTINGS_TYPE_SHIFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getUserIdFromKey(int key) {
|
||||||
|
return key & ~SETTINGS_TYPE_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String settingTypeToString(int type) {
|
||||||
|
switch (type) {
|
||||||
|
case SETTINGS_TYPE_GLOBAL: {
|
||||||
|
return "SETTINGS_GLOBAL";
|
||||||
|
}
|
||||||
|
case SETTINGS_TYPE_SECURE: {
|
||||||
|
return "SETTINGS_SECURE";
|
||||||
|
}
|
||||||
|
case SETTINGS_TYPE_SYSTEM: {
|
||||||
|
return "SETTINGS_SYSTEM";
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String keyToString(int key) {
|
||||||
|
return "Key[user=" + getUserIdFromKey(key) + ";type="
|
||||||
|
+ settingTypeToString(getTypeFromKey(key)) + "]";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreate() {
|
public boolean onCreate() {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
@@ -204,6 +247,7 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
mSettingsRegistry = new SettingsRegistry();
|
mSettingsRegistry = new SettingsRegistry();
|
||||||
}
|
}
|
||||||
registerBroadcastReceivers();
|
registerBroadcastReceivers();
|
||||||
|
startWatchingUserRestrictionChanges();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,28 +257,28 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
switch (method) {
|
switch (method) {
|
||||||
case Settings.CALL_METHOD_GET_GLOBAL: {
|
case Settings.CALL_METHOD_GET_GLOBAL: {
|
||||||
Setting setting = getGlobalSetting(name);
|
Setting setting = getGlobalSetting(name);
|
||||||
return packageValueForCallResult(setting);
|
return packageValueForCallResult(setting, isTrackingGeneration(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
case Settings.CALL_METHOD_GET_SECURE: {
|
case Settings.CALL_METHOD_GET_SECURE: {
|
||||||
Setting setting = getSecureSetting(name, requestingUserId);
|
Setting setting = getSecureSetting(name, requestingUserId);
|
||||||
return packageValueForCallResult(setting);
|
return packageValueForCallResult(setting, isTrackingGeneration(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
case Settings.CALL_METHOD_GET_SYSTEM: {
|
case Settings.CALL_METHOD_GET_SYSTEM: {
|
||||||
Setting setting = getSystemSetting(name, requestingUserId);
|
Setting setting = getSystemSetting(name, requestingUserId);
|
||||||
return packageValueForCallResult(setting);
|
return packageValueForCallResult(setting, isTrackingGeneration(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
case Settings.CALL_METHOD_PUT_GLOBAL: {
|
case Settings.CALL_METHOD_PUT_GLOBAL: {
|
||||||
String value = getSettingValue(args);
|
String value = getSettingValue(args);
|
||||||
insertGlobalSetting(name, value, requestingUserId);
|
insertGlobalSetting(name, value, requestingUserId, false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Settings.CALL_METHOD_PUT_SECURE: {
|
case Settings.CALL_METHOD_PUT_SECURE: {
|
||||||
String value = getSettingValue(args);
|
String value = getSettingValue(args);
|
||||||
insertSecureSetting(name, value, requestingUserId);
|
insertSecureSetting(name, value, requestingUserId, false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -335,13 +379,13 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
|
|
||||||
switch (table) {
|
switch (table) {
|
||||||
case TABLE_GLOBAL: {
|
case TABLE_GLOBAL: {
|
||||||
if (insertGlobalSetting(name, value, UserHandle.getCallingUserId())) {
|
if (insertGlobalSetting(name, value, UserHandle.getCallingUserId(), false)) {
|
||||||
return Uri.withAppendedPath(Settings.Global.CONTENT_URI, name);
|
return Uri.withAppendedPath(Settings.Global.CONTENT_URI, name);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case TABLE_SECURE: {
|
case TABLE_SECURE: {
|
||||||
if (insertSecureSetting(name, value, UserHandle.getCallingUserId())) {
|
if (insertSecureSetting(name, value, UserHandle.getCallingUserId(), false)) {
|
||||||
return Uri.withAppendedPath(Settings.Secure.CONTENT_URI, name);
|
return Uri.withAppendedPath(Settings.Secure.CONTENT_URI, name);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@@ -398,12 +442,12 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
switch (args.table) {
|
switch (args.table) {
|
||||||
case TABLE_GLOBAL: {
|
case TABLE_GLOBAL: {
|
||||||
final int userId = UserHandle.getCallingUserId();
|
final int userId = UserHandle.getCallingUserId();
|
||||||
return deleteGlobalSetting(args.name, userId) ? 1 : 0;
|
return deleteGlobalSetting(args.name, userId, false) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TABLE_SECURE: {
|
case TABLE_SECURE: {
|
||||||
final int userId = UserHandle.getCallingUserId();
|
final int userId = UserHandle.getCallingUserId();
|
||||||
return deleteSecureSetting(args.name, userId) ? 1 : 0;
|
return deleteSecureSetting(args.name, userId, false) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TABLE_SYSTEM: {
|
case TABLE_SYSTEM: {
|
||||||
@@ -439,12 +483,12 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
switch (args.table) {
|
switch (args.table) {
|
||||||
case TABLE_GLOBAL: {
|
case TABLE_GLOBAL: {
|
||||||
final int userId = UserHandle.getCallingUserId();
|
final int userId = UserHandle.getCallingUserId();
|
||||||
return updateGlobalSetting(args.name, value, userId) ? 1 : 0;
|
return updateGlobalSetting(args.name, value, userId, false) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TABLE_SECURE: {
|
case TABLE_SECURE: {
|
||||||
final int userId = UserHandle.getCallingUserId();
|
final int userId = UserHandle.getCallingUserId();
|
||||||
return updateSecureSetting(args.name, value, userId) ? 1 : 0;
|
return updateSecureSetting(args.name, value, userId, false) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TABLE_SYSTEM: {
|
case TABLE_SYSTEM: {
|
||||||
@@ -557,11 +601,15 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
|
|
||||||
switch (intent.getAction()) {
|
switch (intent.getAction()) {
|
||||||
case Intent.ACTION_USER_REMOVED: {
|
case Intent.ACTION_USER_REMOVED: {
|
||||||
|
synchronized (mLock) {
|
||||||
mSettingsRegistry.removeUserStateLocked(userId, true);
|
mSettingsRegistry.removeUserStateLocked(userId, true);
|
||||||
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Intent.ACTION_USER_STOPPED: {
|
case Intent.ACTION_USER_STOPPED: {
|
||||||
|
synchronized (mLock) {
|
||||||
mSettingsRegistry.removeUserStateLocked(userId, false);
|
mSettingsRegistry.removeUserStateLocked(userId, false);
|
||||||
|
}
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -582,6 +630,92 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
UserHandle.ALL, true);
|
UserHandle.ALL, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void startWatchingUserRestrictionChanges() {
|
||||||
|
// TODO: The current design of settings looking different based on user restrictions
|
||||||
|
// should be reworked to keep them separate and system code should check the setting
|
||||||
|
// first followed by checking the user restriction before performing an operation.
|
||||||
|
UserManagerInternal userManager = LocalServices.getService(UserManagerInternal.class);
|
||||||
|
userManager.addUserRestrictionsListener((int userId, Bundle newRestrictions,
|
||||||
|
Bundle prevRestrictions) -> {
|
||||||
|
// We are changing the settings affected by restrictions to their current
|
||||||
|
// value with a forced update to ensure that all cross profile dependencies
|
||||||
|
// are taken into account. Also make sure the settings update to.. the same
|
||||||
|
// value passes the security checks, so clear binder calling id.
|
||||||
|
if (newRestrictions.containsKey(UserManager.DISALLOW_SHARE_LOCATION)
|
||||||
|
!= prevRestrictions.containsKey(UserManager.DISALLOW_SHARE_LOCATION)) {
|
||||||
|
final long identity = Binder.clearCallingIdentity();
|
||||||
|
try {
|
||||||
|
synchronized (mLock) {
|
||||||
|
Setting setting = getSecureSetting(
|
||||||
|
Settings.Secure.LOCATION_PROVIDERS_ALLOWED, userId);
|
||||||
|
updateSecureSetting(Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
|
||||||
|
setting != null ? setting.getValue() : null, userId, true);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
Binder.restoreCallingIdentity(identity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newRestrictions.containsKey(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES)
|
||||||
|
!= prevRestrictions.containsKey(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES)) {
|
||||||
|
final long identity = Binder.clearCallingIdentity();
|
||||||
|
try {
|
||||||
|
synchronized (mLock) {
|
||||||
|
Setting setting = getGlobalSetting(Settings.Global.INSTALL_NON_MARKET_APPS);
|
||||||
|
updateGlobalSetting(Settings.Global.INSTALL_NON_MARKET_APPS,
|
||||||
|
setting != null ? setting.getValue() : null, userId, true);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
Binder.restoreCallingIdentity(identity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newRestrictions.containsKey(UserManager.DISALLOW_DEBUGGING_FEATURES)
|
||||||
|
!= prevRestrictions.containsKey(UserManager.DISALLOW_DEBUGGING_FEATURES)) {
|
||||||
|
final long identity = Binder.clearCallingIdentity();
|
||||||
|
try {
|
||||||
|
synchronized (mLock) {
|
||||||
|
Setting setting = getGlobalSetting(Settings.Global.ADB_ENABLED);
|
||||||
|
updateGlobalSetting(Settings.Global.ADB_ENABLED,
|
||||||
|
setting != null ? setting.getValue() : null, userId, true);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
Binder.restoreCallingIdentity(identity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newRestrictions.containsKey(UserManager.ENSURE_VERIFY_APPS)
|
||||||
|
!= prevRestrictions.containsKey(UserManager.ENSURE_VERIFY_APPS)) {
|
||||||
|
final long identity = Binder.clearCallingIdentity();
|
||||||
|
try {
|
||||||
|
synchronized (mLock) {
|
||||||
|
Setting enable = getGlobalSetting(
|
||||||
|
Settings.Global.PACKAGE_VERIFIER_ENABLE);
|
||||||
|
updateGlobalSetting(Settings.Global.PACKAGE_VERIFIER_ENABLE,
|
||||||
|
enable != null ? enable.getValue() : null, userId, true);
|
||||||
|
Setting include = getGlobalSetting(
|
||||||
|
Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB);
|
||||||
|
updateGlobalSetting(Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB,
|
||||||
|
include != null ? include.getValue() : null, userId, true);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
Binder.restoreCallingIdentity(identity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newRestrictions.containsKey(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)
|
||||||
|
!= prevRestrictions.containsKey(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) {
|
||||||
|
final long identity = Binder.clearCallingIdentity();
|
||||||
|
try {
|
||||||
|
synchronized (mLock) {
|
||||||
|
Setting setting = getGlobalSetting(
|
||||||
|
Settings.Global.PREFERRED_NETWORK_MODE);
|
||||||
|
updateGlobalSetting(Settings.Global.PREFERRED_NETWORK_MODE,
|
||||||
|
setting != null ? setting.getValue() : null, userId, true);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
Binder.restoreCallingIdentity(identity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private Cursor getAllGlobalSettings(String[] projection) {
|
private Cursor getAllGlobalSettings(String[] projection) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.v(LOG_TAG, "getAllGlobalSettings()");
|
Slog.v(LOG_TAG, "getAllGlobalSettings()");
|
||||||
@@ -590,7 +724,7 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
// Get the settings.
|
// Get the settings.
|
||||||
SettingsState settingsState = mSettingsRegistry.getSettingsLocked(
|
SettingsState settingsState = mSettingsRegistry.getSettingsLocked(
|
||||||
SettingsRegistry.SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
|
SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
|
||||||
|
|
||||||
List<String> names = settingsState.getSettingNamesLocked();
|
List<String> names = settingsState.getSettingNamesLocked();
|
||||||
|
|
||||||
@@ -617,34 +751,39 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
|
|
||||||
// Get the value.
|
// Get the value.
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
return mSettingsRegistry.getSettingLocked(SettingsRegistry.SETTINGS_TYPE_GLOBAL,
|
return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_GLOBAL,
|
||||||
UserHandle.USER_SYSTEM, name);
|
UserHandle.USER_SYSTEM, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean updateGlobalSetting(String name, String value, int requestingUserId) {
|
private boolean updateGlobalSetting(String name, String value, int requestingUserId,
|
||||||
|
boolean forceNotify) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.v(LOG_TAG, "updateGlobalSetting(" + name + ", " + value + ")");
|
Slog.v(LOG_TAG, "updateGlobalSetting(" + name + ", " + value + ")");
|
||||||
}
|
}
|
||||||
return mutateGlobalSetting(name, value, requestingUserId, MUTATION_OPERATION_UPDATE);
|
return mutateGlobalSetting(name, value, requestingUserId, MUTATION_OPERATION_UPDATE,
|
||||||
|
forceNotify);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean insertGlobalSetting(String name, String value, int requestingUserId) {
|
private boolean insertGlobalSetting(String name, String value, int requestingUserId,
|
||||||
|
boolean forceNotify) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.v(LOG_TAG, "insertGlobalSetting(" + name + ", " + value + ")");
|
Slog.v(LOG_TAG, "insertGlobalSetting(" + name + ", " + value + ")");
|
||||||
}
|
}
|
||||||
return mutateGlobalSetting(name, value, requestingUserId, MUTATION_OPERATION_INSERT);
|
return mutateGlobalSetting(name, value, requestingUserId, MUTATION_OPERATION_INSERT,
|
||||||
|
forceNotify);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean deleteGlobalSetting(String name, int requestingUserId) {
|
private boolean deleteGlobalSetting(String name, int requestingUserId, boolean forceNotify) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.v(LOG_TAG, "deleteGlobalSettingLocked(" + name + ")");
|
Slog.v(LOG_TAG, "deleteGlobalSettingLocked(" + name + ")");
|
||||||
}
|
}
|
||||||
return mutateGlobalSetting(name, null, requestingUserId, MUTATION_OPERATION_DELETE);
|
return mutateGlobalSetting(name, null, requestingUserId, MUTATION_OPERATION_DELETE,
|
||||||
|
forceNotify);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean mutateGlobalSetting(String name, String value, int requestingUserId,
|
private boolean mutateGlobalSetting(String name, String value, int requestingUserId,
|
||||||
int operation) {
|
int operation, boolean forceNotify) {
|
||||||
// Make sure the caller can change the settings - treated as secure.
|
// Make sure the caller can change the settings - treated as secure.
|
||||||
enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
|
enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
|
||||||
|
|
||||||
@@ -662,20 +801,19 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
switch (operation) {
|
switch (operation) {
|
||||||
case MUTATION_OPERATION_INSERT: {
|
case MUTATION_OPERATION_INSERT: {
|
||||||
return mSettingsRegistry
|
return mSettingsRegistry
|
||||||
.insertSettingLocked(SettingsRegistry.SETTINGS_TYPE_GLOBAL,
|
.insertSettingLocked(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM,
|
||||||
UserHandle.USER_SYSTEM, name, value, getCallingPackage());
|
name, value, getCallingPackage(), forceNotify);
|
||||||
}
|
}
|
||||||
|
|
||||||
case MUTATION_OPERATION_DELETE: {
|
case MUTATION_OPERATION_DELETE: {
|
||||||
return mSettingsRegistry.deleteSettingLocked(
|
return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_GLOBAL,
|
||||||
SettingsRegistry.SETTINGS_TYPE_GLOBAL,
|
UserHandle.USER_SYSTEM, name, forceNotify);
|
||||||
UserHandle.USER_SYSTEM, name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case MUTATION_OPERATION_UPDATE: {
|
case MUTATION_OPERATION_UPDATE: {
|
||||||
return mSettingsRegistry
|
return mSettingsRegistry
|
||||||
.updateSettingLocked(SettingsRegistry.SETTINGS_TYPE_GLOBAL,
|
.updateSettingLocked(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM,
|
||||||
UserHandle.USER_SYSTEM, name, value, getCallingPackage());
|
name, value, getCallingPackage(), forceNotify);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -693,7 +831,7 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
|
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
List<String> names = mSettingsRegistry.getSettingsNamesLocked(
|
List<String> names = mSettingsRegistry.getSettingsNamesLocked(
|
||||||
SettingsRegistry.SETTINGS_TYPE_SECURE, callingUserId);
|
SETTINGS_TYPE_SECURE, callingUserId);
|
||||||
|
|
||||||
final int nameCount = names.size();
|
final int nameCount = names.size();
|
||||||
|
|
||||||
@@ -712,7 +850,7 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Setting setting = mSettingsRegistry.getSettingLocked(
|
Setting setting = mSettingsRegistry.getSettingLocked(
|
||||||
SettingsRegistry.SETTINGS_TYPE_SECURE, owningUserId, name);
|
SETTINGS_TYPE_SECURE, owningUserId, name);
|
||||||
appendSettingToCursor(result, setting);
|
appendSettingToCursor(result, setting);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -738,39 +876,44 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
|
|
||||||
// Get the value.
|
// Get the value.
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
return mSettingsRegistry.getSettingLocked(SettingsRegistry.SETTINGS_TYPE_SECURE,
|
return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SECURE,
|
||||||
owningUserId, name);
|
owningUserId, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean insertSecureSetting(String name, String value, int requestingUserId) {
|
private boolean insertSecureSetting(String name, String value, int requestingUserId,
|
||||||
|
boolean forceNotify) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.v(LOG_TAG, "insertSecureSetting(" + name + ", " + value + ", "
|
Slog.v(LOG_TAG, "insertSecureSetting(" + name + ", " + value + ", "
|
||||||
+ requestingUserId + ")");
|
+ requestingUserId + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
return mutateSecureSetting(name, value, requestingUserId, MUTATION_OPERATION_INSERT);
|
return mutateSecureSetting(name, value, requestingUserId, MUTATION_OPERATION_INSERT,
|
||||||
|
forceNotify);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean deleteSecureSetting(String name, int requestingUserId) {
|
private boolean deleteSecureSetting(String name, int requestingUserId, boolean forceNotify) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.v(LOG_TAG, "deleteSecureSetting(" + name + ", " + requestingUserId + ")");
|
Slog.v(LOG_TAG, "deleteSecureSetting(" + name + ", " + requestingUserId + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
return mutateSecureSetting(name, null, requestingUserId, MUTATION_OPERATION_DELETE);
|
return mutateSecureSetting(name, null, requestingUserId, MUTATION_OPERATION_DELETE,
|
||||||
|
forceNotify);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean updateSecureSetting(String name, String value, int requestingUserId) {
|
private boolean updateSecureSetting(String name, String value, int requestingUserId,
|
||||||
|
boolean forceNotify) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Slog.v(LOG_TAG, "updateSecureSetting(" + name + ", " + value + ", "
|
Slog.v(LOG_TAG, "updateSecureSetting(" + name + ", " + value + ", "
|
||||||
+ requestingUserId + ")");
|
+ requestingUserId + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
return mutateSecureSetting(name, value, requestingUserId, MUTATION_OPERATION_UPDATE);
|
return mutateSecureSetting(name, value, requestingUserId, MUTATION_OPERATION_UPDATE,
|
||||||
|
forceNotify);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean mutateSecureSetting(String name, String value, int requestingUserId,
|
private boolean mutateSecureSetting(String name, String value, int requestingUserId,
|
||||||
int operation) {
|
int operation, boolean forceNotify) {
|
||||||
// Make sure the caller can change the settings.
|
// Make sure the caller can change the settings.
|
||||||
enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
|
enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
|
||||||
|
|
||||||
@@ -793,28 +936,25 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
|
|
||||||
// Special cases for location providers (sigh).
|
// Special cases for location providers (sigh).
|
||||||
if (Settings.Secure.LOCATION_PROVIDERS_ALLOWED.equals(name)) {
|
if (Settings.Secure.LOCATION_PROVIDERS_ALLOWED.equals(name)) {
|
||||||
return updateLocationProvidersAllowedLocked(value, owningUserId);
|
return updateLocationProvidersAllowedLocked(value, owningUserId, forceNotify);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mutate the value.
|
// Mutate the value.
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
switch (operation) {
|
switch (operation) {
|
||||||
case MUTATION_OPERATION_INSERT: {
|
case MUTATION_OPERATION_INSERT: {
|
||||||
return mSettingsRegistry
|
return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SECURE,
|
||||||
.insertSettingLocked(SettingsRegistry.SETTINGS_TYPE_SECURE,
|
owningUserId, name, value, getCallingPackage(), forceNotify);
|
||||||
owningUserId, name, value, getCallingPackage());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case MUTATION_OPERATION_DELETE: {
|
case MUTATION_OPERATION_DELETE: {
|
||||||
return mSettingsRegistry.deleteSettingLocked(
|
return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_SECURE,
|
||||||
SettingsRegistry.SETTINGS_TYPE_SECURE,
|
owningUserId, name, forceNotify);
|
||||||
owningUserId, name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case MUTATION_OPERATION_UPDATE: {
|
case MUTATION_OPERATION_UPDATE: {
|
||||||
return mSettingsRegistry
|
return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_SECURE,
|
||||||
.updateSettingLocked(SettingsRegistry.SETTINGS_TYPE_SECURE,
|
owningUserId, name, value, getCallingPackage(), forceNotify);
|
||||||
owningUserId, name, value, getCallingPackage());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -832,7 +972,7 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
|
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
List<String> names = mSettingsRegistry.getSettingsNamesLocked(
|
List<String> names = mSettingsRegistry.getSettingsNamesLocked(
|
||||||
SettingsRegistry.SETTINGS_TYPE_SYSTEM, callingUserId);
|
SETTINGS_TYPE_SYSTEM, callingUserId);
|
||||||
|
|
||||||
final int nameCount = names.size();
|
final int nameCount = names.size();
|
||||||
|
|
||||||
@@ -847,7 +987,7 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
name);
|
name);
|
||||||
|
|
||||||
Setting setting = mSettingsRegistry.getSettingLocked(
|
Setting setting = mSettingsRegistry.getSettingLocked(
|
||||||
SettingsRegistry.SETTINGS_TYPE_SYSTEM, owningUserId, name);
|
SETTINGS_TYPE_SYSTEM, owningUserId, name);
|
||||||
appendSettingToCursor(result, setting);
|
appendSettingToCursor(result, setting);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -868,8 +1008,7 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
|
|
||||||
// Get the value.
|
// Get the value.
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
return mSettingsRegistry.getSettingLocked(SettingsRegistry.SETTINGS_TYPE_SYSTEM,
|
return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SYSTEM, owningUserId, name);
|
||||||
owningUserId, name);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -944,22 +1083,19 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
switch (operation) {
|
switch (operation) {
|
||||||
case MUTATION_OPERATION_INSERT: {
|
case MUTATION_OPERATION_INSERT: {
|
||||||
validateSystemSettingValue(name, value);
|
validateSystemSettingValue(name, value);
|
||||||
return mSettingsRegistry
|
return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SYSTEM,
|
||||||
.insertSettingLocked(SettingsRegistry.SETTINGS_TYPE_SYSTEM,
|
owningUserId, name, value, getCallingPackage(), false);
|
||||||
owningUserId, name, value, getCallingPackage());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case MUTATION_OPERATION_DELETE: {
|
case MUTATION_OPERATION_DELETE: {
|
||||||
return mSettingsRegistry.deleteSettingLocked(
|
return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_SYSTEM,
|
||||||
SettingsRegistry.SETTINGS_TYPE_SYSTEM,
|
owningUserId, name, false);
|
||||||
owningUserId, name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case MUTATION_OPERATION_UPDATE: {
|
case MUTATION_OPERATION_UPDATE: {
|
||||||
validateSystemSettingValue(name, value);
|
validateSystemSettingValue(name, value);
|
||||||
return mSettingsRegistry
|
return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_SYSTEM,
|
||||||
.updateSettingLocked(SettingsRegistry.SETTINGS_TYPE_SYSTEM,
|
owningUserId, name, value, getCallingPackage(), false);
|
||||||
owningUserId, name, value, getCallingPackage());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1003,8 +1139,8 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
* Checks whether changing a setting to a value is prohibited by the corresponding user
|
* Checks whether changing a setting to a value is prohibited by the corresponding user
|
||||||
* restriction.
|
* restriction.
|
||||||
*
|
*
|
||||||
* <p>See also {@link com.android.server.pm.UserRestrictionsUtils#applyUserRestrictionLR},
|
* <p>See also {@link com.android.server.pm.UserRestrictionsUtils#applyUserRestriction(
|
||||||
* which should be in sync with this method.
|
* Context, int, String, boolean)}, which should be in sync with this method.
|
||||||
*
|
*
|
||||||
* @return true if the change is prohibited, false if the change is allowed.
|
* @return true if the change is prohibited, false if the change is allowed.
|
||||||
*/
|
*/
|
||||||
@@ -1177,13 +1313,19 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
*
|
*
|
||||||
* @returns whether the enabled location providers changed.
|
* @returns whether the enabled location providers changed.
|
||||||
*/
|
*/
|
||||||
private boolean updateLocationProvidersAllowedLocked(String value, int owningUserId) {
|
private boolean updateLocationProvidersAllowedLocked(String value, int owningUserId,
|
||||||
|
boolean forceNotify) {
|
||||||
if (TextUtils.isEmpty(value)) {
|
if (TextUtils.isEmpty(value)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final char prefix = value.charAt(0);
|
final char prefix = value.charAt(0);
|
||||||
if (prefix != '+' && prefix != '-') {
|
if (prefix != '+' && prefix != '-') {
|
||||||
|
if (forceNotify) {
|
||||||
|
final int key = makeKey(SETTINGS_TYPE_SECURE, owningUserId);
|
||||||
|
mSettingsRegistry.notifyForSettingsChange(key,
|
||||||
|
Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1232,12 +1374,17 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// nothing changed, so no need to update the database
|
// nothing changed, so no need to update the database
|
||||||
|
if (forceNotify) {
|
||||||
|
final int key = makeKey(SETTINGS_TYPE_SECURE, owningUserId);
|
||||||
|
mSettingsRegistry.notifyForSettingsChange(key,
|
||||||
|
Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return mSettingsRegistry.insertSettingLocked(SettingsRegistry.SETTINGS_TYPE_SECURE,
|
return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SECURE,
|
||||||
owningUserId, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, newProviders,
|
owningUserId, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, newProviders,
|
||||||
getCallingPackage());
|
getCallingPackage(), forceNotify);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk(
|
private static void warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk(
|
||||||
@@ -1270,12 +1417,20 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
"get/set setting for user", null);
|
"get/set setting for user", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Bundle packageValueForCallResult(Setting setting) {
|
private Bundle packageValueForCallResult(Setting setting,
|
||||||
if (setting == null) {
|
boolean trackingGeneration) {
|
||||||
return NULL_SETTING;
|
if (!trackingGeneration) {
|
||||||
|
if (setting.isNull()) {
|
||||||
|
return NULL_SETTING_BUNDLE;
|
||||||
}
|
}
|
||||||
return Bundle.forPair(Settings.NameValueTable.VALUE, setting.getValue());
|
return Bundle.forPair(Settings.NameValueTable.VALUE, setting.getValue());
|
||||||
}
|
}
|
||||||
|
Bundle result = new Bundle();
|
||||||
|
result.putString(Settings.NameValueTable.VALUE,
|
||||||
|
!setting.isNull() ? setting.getValue() : null);
|
||||||
|
mSettingsRegistry.mGenerationRegistry.addGenerationData(result, setting.getkey());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private static int getRequestingUserId(Bundle args) {
|
private static int getRequestingUserId(Bundle args) {
|
||||||
final int callingUserId = UserHandle.getCallingUserId();
|
final int callingUserId = UserHandle.getCallingUserId();
|
||||||
@@ -1283,6 +1438,10 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
: callingUserId;
|
: callingUserId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isTrackingGeneration(Bundle args) {
|
||||||
|
return args != null && args.containsKey(Settings.CALL_METHOD_TRACK_GENERATION_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
private static String getSettingValue(Bundle args) {
|
private static String getSettingValue(Bundle args) {
|
||||||
return (args != null) ? args.getString(Settings.NameValueTable.VALUE) : null;
|
return (args != null) ? args.getString(Settings.NameValueTable.VALUE) : null;
|
||||||
}
|
}
|
||||||
@@ -1448,26 +1607,22 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
final class SettingsRegistry {
|
final class SettingsRegistry {
|
||||||
private static final String DROPBOX_TAG_USERLOG = "restricted_profile_ssaid";
|
private static final String DROPBOX_TAG_USERLOG = "restricted_profile_ssaid";
|
||||||
|
|
||||||
private static final int SETTINGS_TYPE_GLOBAL = 0;
|
|
||||||
private static final int SETTINGS_TYPE_SYSTEM = 1;
|
|
||||||
private static final int SETTINGS_TYPE_SECURE = 2;
|
|
||||||
|
|
||||||
private static final int SETTINGS_TYPE_MASK = 0xF0000000;
|
|
||||||
private static final int SETTINGS_TYPE_SHIFT = 28;
|
|
||||||
|
|
||||||
private static final String SETTINGS_FILE_GLOBAL = "settings_global.xml";
|
private static final String SETTINGS_FILE_GLOBAL = "settings_global.xml";
|
||||||
private static final String SETTINGS_FILE_SYSTEM = "settings_system.xml";
|
private static final String SETTINGS_FILE_SYSTEM = "settings_system.xml";
|
||||||
private static final String SETTINGS_FILE_SECURE = "settings_secure.xml";
|
private static final String SETTINGS_FILE_SECURE = "settings_secure.xml";
|
||||||
|
|
||||||
private final SparseArray<SettingsState> mSettingsStates = new SparseArray<>();
|
private final SparseArray<SettingsState> mSettingsStates = new SparseArray<>();
|
||||||
|
|
||||||
private final BackupManager mBackupManager;
|
private GenerationRegistry mGenerationRegistry;
|
||||||
|
|
||||||
private final Handler mHandler;
|
private final Handler mHandler;
|
||||||
|
|
||||||
|
private final BackupManager mBackupManager;
|
||||||
|
|
||||||
public SettingsRegistry() {
|
public SettingsRegistry() {
|
||||||
mBackupManager = new BackupManager(getContext());
|
|
||||||
mHandler = new MyHandler(getContext().getMainLooper());
|
mHandler = new MyHandler(getContext().getMainLooper());
|
||||||
|
mGenerationRegistry = new GenerationRegistry(mLock);
|
||||||
|
mBackupManager = new BackupManager(getContext());
|
||||||
migrateAllLegacySettingsIfNeeded();
|
migrateAllLegacySettingsIfNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1554,28 +1709,31 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Nuke generation tracking data
|
||||||
|
mGenerationRegistry.onUserRemoved(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean insertSettingLocked(int type, int userId, String name, String value,
|
public boolean insertSettingLocked(int type, int userId, String name, String value,
|
||||||
String packageName) {
|
String packageName, boolean forceNotify) {
|
||||||
final int key = makeKey(type, userId);
|
final int key = makeKey(type, userId);
|
||||||
|
|
||||||
SettingsState settingsState = peekSettingsStateLocked(key);
|
SettingsState settingsState = peekSettingsStateLocked(key);
|
||||||
final boolean success = settingsState.insertSettingLocked(name, value, packageName);
|
final boolean success = settingsState.insertSettingLocked(name, value, packageName);
|
||||||
|
|
||||||
if (success) {
|
if (forceNotify || success) {
|
||||||
notifyForSettingsChange(key, name);
|
notifyForSettingsChange(key, name);
|
||||||
}
|
}
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean deleteSettingLocked(int type, int userId, String name) {
|
public boolean deleteSettingLocked(int type, int userId, String name, boolean forceNotify) {
|
||||||
final int key = makeKey(type, userId);
|
final int key = makeKey(type, userId);
|
||||||
|
|
||||||
SettingsState settingsState = peekSettingsStateLocked(key);
|
SettingsState settingsState = peekSettingsStateLocked(key);
|
||||||
final boolean success = settingsState.deleteSettingLocked(name);
|
final boolean success = settingsState.deleteSettingLocked(name);
|
||||||
|
|
||||||
if (success) {
|
if (forceNotify || success) {
|
||||||
notifyForSettingsChange(key, name);
|
notifyForSettingsChange(key, name);
|
||||||
}
|
}
|
||||||
return success;
|
return success;
|
||||||
@@ -1589,13 +1747,13 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean updateSettingLocked(int type, int userId, String name, String value,
|
public boolean updateSettingLocked(int type, int userId, String name, String value,
|
||||||
String packageName) {
|
String packageName, boolean forceNotify) {
|
||||||
final int key = makeKey(type, userId);
|
final int key = makeKey(type, userId);
|
||||||
|
|
||||||
SettingsState settingsState = peekSettingsStateLocked(key);
|
SettingsState settingsState = peekSettingsStateLocked(key);
|
||||||
final boolean success = settingsState.updateSettingLocked(name, value, packageName);
|
final boolean success = settingsState.updateSettingLocked(name, value, packageName);
|
||||||
|
|
||||||
if (success) {
|
if (forceNotify || success) {
|
||||||
notifyForSettingsChange(key, name);
|
notifyForSettingsChange(key, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1786,38 +1944,6 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void notifyForSettingsChange(int key, String name) {
|
private void notifyForSettingsChange(int key, String name) {
|
||||||
// Update the system property *first*, so if someone is listening for
|
|
||||||
// a notification and then using the contract class to get their data,
|
|
||||||
// the system property will be updated and they'll get the new data.
|
|
||||||
|
|
||||||
boolean backedUpDataChanged = false;
|
|
||||||
String property = null;
|
|
||||||
if (isGlobalSettingsKey(key)) {
|
|
||||||
property = Settings.Global.SYS_PROP_SETTING_VERSION;
|
|
||||||
backedUpDataChanged = true;
|
|
||||||
} else if (isSecureSettingsKey(key)) {
|
|
||||||
property = Settings.Secure.SYS_PROP_SETTING_VERSION;
|
|
||||||
backedUpDataChanged = true;
|
|
||||||
} else if (isSystemSettingsKey(key)) {
|
|
||||||
property = Settings.System.SYS_PROP_SETTING_VERSION;
|
|
||||||
backedUpDataChanged = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (property != null) {
|
|
||||||
final long version = SystemProperties.getLong(property, 0) + 1;
|
|
||||||
SystemProperties.set(property, Long.toString(version));
|
|
||||||
if (DEBUG) {
|
|
||||||
Slog.v(LOG_TAG, "System property " + property + "=" + version);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inform the backup manager about a data change
|
|
||||||
if (backedUpDataChanged) {
|
|
||||||
mHandler.obtainMessage(MyHandler.MSG_NOTIFY_DATA_CHANGED).sendToTarget();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now send the notification through the content framework.
|
|
||||||
|
|
||||||
final int userId = getUserIdFromKey(key);
|
final int userId = getUserIdFromKey(key);
|
||||||
Uri uri = getNotificationUriFor(key, name);
|
Uri uri = getNotificationUriFor(key, name);
|
||||||
|
|
||||||
@@ -1825,13 +1951,19 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
userId, 0, uri).sendToTarget();
|
userId, 0, uri).sendToTarget();
|
||||||
|
|
||||||
if (isSecureSettingsKey(key)) {
|
if (isSecureSettingsKey(key)) {
|
||||||
maybeNotifyProfiles(userId, uri, name, sSecureCloneToManagedSettings);
|
maybeNotifyProfiles(getTypeFromKey(key), userId, uri, name,
|
||||||
|
sSecureCloneToManagedSettings);
|
||||||
} else if (isSystemSettingsKey(key)) {
|
} else if (isSystemSettingsKey(key)) {
|
||||||
maybeNotifyProfiles(userId, uri, name, sSystemCloneToManagedSettings);
|
maybeNotifyProfiles(getTypeFromKey(key), userId, uri, name,
|
||||||
}
|
sSystemCloneToManagedSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void maybeNotifyProfiles(int userId, Uri uri, String name,
|
mGenerationRegistry.incrementGeneration(key);
|
||||||
|
|
||||||
|
mHandler.obtainMessage(MyHandler.MSG_NOTIFY_DATA_CHANGED).sendToTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void maybeNotifyProfiles(int type, int userId, Uri uri, String name,
|
||||||
Set<String> keysCloned) {
|
Set<String> keysCloned) {
|
||||||
if (keysCloned.contains(name)) {
|
if (keysCloned.contains(name)) {
|
||||||
for (int profileId : mUserManager.getProfileIdsWithDisabled(userId)) {
|
for (int profileId : mUserManager.getProfileIdsWithDisabled(userId)) {
|
||||||
@@ -1839,21 +1971,13 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
if (profileId != userId) {
|
if (profileId != userId) {
|
||||||
mHandler.obtainMessage(MyHandler.MSG_NOTIFY_URI_CHANGED,
|
mHandler.obtainMessage(MyHandler.MSG_NOTIFY_URI_CHANGED,
|
||||||
profileId, 0, uri).sendToTarget();
|
profileId, 0, uri).sendToTarget();
|
||||||
}
|
final int key = makeKey(type, profileId);
|
||||||
}
|
mGenerationRegistry.incrementGeneration(key);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int makeKey(int type, int userId) {
|
mHandler.obtainMessage(MyHandler.MSG_NOTIFY_DATA_CHANGED).sendToTarget();
|
||||||
return (type << SETTINGS_TYPE_SHIFT) | userId;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getTypeFromKey(int key) {
|
|
||||||
return key >> SETTINGS_TYPE_SHIFT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getUserIdFromKey(int key) {
|
|
||||||
return key & ~SETTINGS_TYPE_MASK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isGlobalSettingsKey(int key) {
|
private boolean isGlobalSettingsKey(int key) {
|
||||||
@@ -1953,7 +2077,7 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
public void upgradeIfNeededLocked() {
|
public void upgradeIfNeededLocked() {
|
||||||
// The version of all settings for a user is the same (all users have secure).
|
// The version of all settings for a user is the same (all users have secure).
|
||||||
SettingsState secureSettings = getSettingsLocked(
|
SettingsState secureSettings = getSettingsLocked(
|
||||||
SettingsRegistry.SETTINGS_TYPE_SECURE, mUserId);
|
SETTINGS_TYPE_SECURE, mUserId);
|
||||||
|
|
||||||
// Try an update from the current state.
|
// Try an update from the current state.
|
||||||
final int oldVersion = secureSettings.getVersionLocked();
|
final int oldVersion = secureSettings.getVersionLocked();
|
||||||
@@ -1987,7 +2111,7 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
// Set the global settings version if owner.
|
// Set the global settings version if owner.
|
||||||
if (mUserId == UserHandle.USER_SYSTEM) {
|
if (mUserId == UserHandle.USER_SYSTEM) {
|
||||||
SettingsState globalSettings = getSettingsLocked(
|
SettingsState globalSettings = getSettingsLocked(
|
||||||
SettingsRegistry.SETTINGS_TYPE_GLOBAL, mUserId);
|
SETTINGS_TYPE_GLOBAL, mUserId);
|
||||||
globalSettings.setVersionLocked(newVersion);
|
globalSettings.setVersionLocked(newVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1996,7 +2120,7 @@ public class SettingsProvider extends ContentProvider {
|
|||||||
|
|
||||||
// Set the system settings version.
|
// Set the system settings version.
|
||||||
SettingsState systemSettings = getSettingsLocked(
|
SettingsState systemSettings = getSettingsLocked(
|
||||||
SettingsRegistry.SETTINGS_TYPE_SYSTEM, mUserId);
|
SETTINGS_TYPE_SYSTEM, mUserId);
|
||||||
systemSettings.setVersionLocked(newVersion);
|
systemSettings.setVersionLocked(newVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.providers.settings;
|
package com.android.providers.settings;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
@@ -109,6 +110,14 @@ final class SettingsState {
|
|||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private final File mStatePersistFile;
|
private final File mStatePersistFile;
|
||||||
|
|
||||||
|
private final Setting mNullSetting = new Setting(null, null, null) {
|
||||||
|
@Override
|
||||||
|
public boolean isNull() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@GuardedBy("mLock")
|
||||||
public final int mKey;
|
public final int mKey;
|
||||||
|
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
@@ -198,9 +207,13 @@ final class SettingsState {
|
|||||||
// The settings provider must hold its lock when calling here.
|
// The settings provider must hold its lock when calling here.
|
||||||
public Setting getSettingLocked(String name) {
|
public Setting getSettingLocked(String name) {
|
||||||
if (TextUtils.isEmpty(name)) {
|
if (TextUtils.isEmpty(name)) {
|
||||||
return null;
|
return mNullSetting;
|
||||||
}
|
}
|
||||||
return mSettings.get(name);
|
Setting setting = mSettings.get(name);
|
||||||
|
if (setting != null) {
|
||||||
|
return new Setting(setting);
|
||||||
|
}
|
||||||
|
return mNullSetting;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The settings provider must hold its lock when calling here.
|
// The settings provider must hold its lock when calling here.
|
||||||
@@ -549,12 +562,19 @@ final class SettingsState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class Setting {
|
class Setting {
|
||||||
private String name;
|
private String name;
|
||||||
private String value;
|
private String value;
|
||||||
private String packageName;
|
private String packageName;
|
||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
|
public Setting(Setting other) {
|
||||||
|
name = other.name;
|
||||||
|
value = other.value;
|
||||||
|
packageName = other.packageName;
|
||||||
|
id = other.id;
|
||||||
|
}
|
||||||
|
|
||||||
public Setting(String name, String value, String packageName) {
|
public Setting(String name, String value, String packageName) {
|
||||||
init(name, value, packageName, String.valueOf(mNextId++));
|
init(name, value, packageName, String.valueOf(mNextId++));
|
||||||
}
|
}
|
||||||
@@ -575,6 +595,10 @@ final class SettingsState {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getkey() {
|
||||||
|
return mKey;
|
||||||
|
}
|
||||||
|
|
||||||
public String getValue() {
|
public String getValue() {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@@ -587,6 +611,10 @@ final class SettingsState {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isNull() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean update(String value, String packageName) {
|
public boolean update(String value, String packageName) {
|
||||||
if (Objects.equal(value, this.value)) {
|
if (Objects.equal(value, this.value)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -370,19 +370,6 @@ public class UserRestrictionsUtils {
|
|||||||
android.provider.Settings.Secure.LOCATION_MODE_OFF,
|
android.provider.Settings.Secure.LOCATION_MODE_OFF,
|
||||||
userId);
|
userId);
|
||||||
}
|
}
|
||||||
// Send out notifications as some clients may want to reread the
|
|
||||||
// value which actually changed due to a restriction having been
|
|
||||||
// applied.
|
|
||||||
final String property =
|
|
||||||
android.provider.Settings.Secure.SYS_PROP_SETTING_VERSION;
|
|
||||||
long version = SystemProperties.getLong(property, 0) + 1;
|
|
||||||
SystemProperties.set(property, Long.toString(version));
|
|
||||||
|
|
||||||
final String name = android.provider.Settings.Secure.LOCATION_PROVIDERS_ALLOWED;
|
|
||||||
final Uri url = Uri.withAppendedPath(
|
|
||||||
android.provider.Settings.Secure.CONTENT_URI, name);
|
|
||||||
context.getContentResolver().notifyChange(url, null, true, userId);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case UserManager.DISALLOW_DEBUGGING_FEATURES:
|
case UserManager.DISALLOW_DEBUGGING_FEATURES:
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
|
|||||||
Reference in New Issue
Block a user