Merge "Reads "enable_cursor_control" device config flag in Editor."
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index b891af5..513e72f 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -21,6 +21,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.AppGlobals;
 import android.app.PendingIntent;
 import android.app.PendingIntent.CanceledException;
 import android.app.RemoteAction;
@@ -384,6 +385,10 @@
 
     private final SuggestionHelper mSuggestionHelper = new SuggestionHelper();
 
+    // Specifies whether the cursor control feature set is enabled.
+    // This can only be true if the text view is editable.
+    private final boolean mCursorControlEnabled;
+
     Editor(TextView textView) {
         mTextView = textView;
         // Synchronize the filter list, which places the undo input filter at the end.
@@ -397,6 +402,13 @@
                     Magnifier.createBuilderWithOldMagnifierDefaults(mTextView).build();
             mMagnifierAnimator = new MagnifierMotionAnimator(magnifier);
         }
+
+        mCursorControlEnabled = AppGlobals.getIntCoreSetting(
+                WidgetFlags.KEY_ENABLE_CURSOR_CONTROL , 0) != 0;
+        if (TextView.DEBUG_CURSOR) {
+            logCursor("Editor", "Cursor control is %s.",
+                    mCursorControlEnabled ? "enabled" : "disabled");
+        }
     }
 
     ParcelableParcel saveInstanceState() {
diff --git a/core/java/android/widget/WidgetFlags.java b/core/java/android/widget/WidgetFlags.java
new file mode 100644
index 0000000..fa1e498d
--- /dev/null
+++ b/core/java/android/widget/WidgetFlags.java
@@ -0,0 +1,40 @@
+/*
+  Copyright (C) 2019 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.widget;
+
+/**
+ * Keeps the flags related to the Widget namespace in {@link DeviceConfig}.
+ *
+ * @hide
+ */
+public final class WidgetFlags {
+
+    /**
+     * Whether the cursor control feature set is enabled.
+     * TODO: Makes this flag key visible to webview/chrome.
+     */
+    public static final String ENABLE_CURSOR_CONTROL =
+            "CursorControlFeature__enable_cursor_control";
+
+    /**
+     * The key name used in app core settings for enable cursor control.
+     */
+    public static final String KEY_ENABLE_CURSOR_CONTROL = "widget__enable_cursor_control";
+
+    private WidgetFlags() {
+    }
+}
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index aa8bc04..fa55701 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -16,15 +16,21 @@
 
 package com.android.server.am;
 
+import android.app.ActivityThread;
 import android.content.Context;
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Bundle;
+import android.provider.DeviceConfig;
 import android.provider.Settings;
+import android.widget.WidgetFlags;
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -36,6 +42,19 @@
 final class CoreSettingsObserver extends ContentObserver {
     private static final String LOG_TAG = CoreSettingsObserver.class.getSimpleName();
 
+    private static class DeviceConfigEntry {
+        String namespace;
+        String flag;
+        String coreSettingKey;
+        Class<?> type;
+        DeviceConfigEntry(String namespace, String flag, String coreSettingKey, Class<?> type) {
+            this.namespace = namespace;
+            this.flag = flag;
+            this.coreSettingKey = coreSettingKey;
+            this.type = type;
+        }
+    }
+
     // mapping form property name to its type
     @VisibleForTesting
     static final Map<String, Class<?>> sSecureSettingToTypeMap = new HashMap<
@@ -46,6 +65,7 @@
     @VisibleForTesting
     static final Map<String, Class<?>> sGlobalSettingToTypeMap = new HashMap<
             String, Class<?>>();
+    static final List<DeviceConfigEntry> sDeviceConfigEntries = new ArrayList<DeviceConfigEntry>();
     static {
         sSecureSettingToTypeMap.put(Settings.Secure.LONG_PRESS_TIMEOUT, int.class);
         sSecureSettingToTypeMap.put(Settings.Secure.MULTI_PRESS_TIMEOUT, int.class);
@@ -84,6 +104,11 @@
         sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_BLACKLISTS, String.class);
         sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES, String.class);
         // add other global settings here...
+
+        sDeviceConfigEntries.add(new DeviceConfigEntry(
+                DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.ENABLE_CURSOR_CONTROL,
+                WidgetFlags.KEY_ENABLE_CURSOR_CONTROL, boolean.class));
+        // add other device configs here...
     }
 
     private final Bundle mCoreSettings = new Bundle();
@@ -112,6 +137,7 @@
         populateSettings(mCoreSettings, sSecureSettingToTypeMap);
         populateSettings(mCoreSettings, sSystemSettingToTypeMap);
         populateSettings(mCoreSettings, sGlobalSettingToTypeMap);
+        populateSettingsFromDeviceConfig();
         mActivityManagerService.onCoreSettingsChange(mCoreSettings);
     }
 
@@ -133,6 +159,16 @@
             mActivityManagerService.mContext.getContentResolver().registerContentObserver(
                     uri, false, this);
         }
+
+        HashSet<String> deviceConfigNamespaces = new HashSet<>();
+        for (DeviceConfigEntry entry : sDeviceConfigEntries) {
+            if (!deviceConfigNamespaces.contains(entry.namespace)) {
+                DeviceConfig.addOnPropertiesChangedListener(
+                        entry.namespace, ActivityThread.currentApplication().getMainExecutor(),
+                        (DeviceConfig.Properties prop) -> onChange(false));
+                deviceConfigNamespaces.add(entry.namespace);
+            }
+        }
     }
 
     @VisibleForTesting
@@ -164,4 +200,25 @@
             }
         }
     }
+
+    private void populateSettingsFromDeviceConfig() {
+        for (DeviceConfigEntry entry : sDeviceConfigEntries) {
+            if (entry.type == String.class) {
+                mCoreSettings.putString(entry.coreSettingKey,
+                        DeviceConfig.getString(entry.namespace, entry.flag, ""));
+            } else if (entry.type == int.class) {
+                mCoreSettings.putInt(entry.coreSettingKey,
+                        DeviceConfig.getInt(entry.namespace, entry.flag, 0));
+            } else if (entry.type == float.class) {
+                mCoreSettings.putFloat(entry.coreSettingKey,
+                        DeviceConfig.getFloat(entry.namespace, entry.flag, 0));
+            } else if (entry.type == long.class) {
+                mCoreSettings.putLong(entry.coreSettingKey,
+                        DeviceConfig.getLong(entry.namespace, entry.flag, 0));
+            } else if (entry.type == boolean.class) {
+                mCoreSettings.putInt(entry.coreSettingKey,
+                        DeviceConfig.getBoolean(entry.namespace, entry.flag, false) ? 1 : 0);
+            }
+        }
+    }
 }