Merge changes from topic "jumbo-sidecar-posture" into sc-dev

* changes:
  Update sample Sidecar/Extension implementation to allow mapping system device state to posture via a config.
  Seperate SettingsConfigProvider into producers.
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b0327a5..b4ef63f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4649,6 +4649,10 @@
     <!-- WindowsManager JetPack display features -->
     <string name="config_display_features" translatable="false" />
 
+    <!-- Map of System DeviceState supplied by DeviceStateManager to WM Jetpack posture. Must be in
+         the format [System DeviceState]:[WM Jetpack Posture], for example: "0:1". -->
+    <string-array name="config_device_state_postures" translatable="false" />
+
     <!-- Aspect ratio of letterboxing for fixed orientation. Values <= 1.0 will be ignored.
          Note: Activity min/max aspect ratio restrictions will still be respected.
          Therefore this override can control the maximum screen area that can be occupied by
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index cdeb71c..1d74d85 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4182,6 +4182,7 @@
   <java-symbol type="dimen" name="default_background_blur_radius" />
   <java-symbol type="array" name="config_keep_warming_services" />
   <java-symbol type="string" name="config_display_features" />
+  <java-symbol type="array" name="config_device_state_postures" />
 
   <java-symbol type="dimen" name="controls_thumbnail_image_max_height" />
   <java-symbol type="dimen" name="controls_thumbnail_image_max_width" />
diff --git a/libs/WindowManager/Jetpack/Android.bp b/libs/WindowManager/Jetpack/Android.bp
index d8f00bb..dc4b563 100644
--- a/libs/WindowManager/Jetpack/Android.bp
+++ b/libs/WindowManager/Jetpack/Android.bp
@@ -30,13 +30,20 @@
 
 java_library {
     name: "androidx.window.sidecar",
-    srcs: ["src/androidx/window/sidecar/**/*.java", "src/androidx/window/util/**/*.java"],
+    srcs: [
+        "src/androidx/window/sidecar/**/*.java",
+        "src/androidx/window/util/**/*.java",
+        "src/androidx/window/common/**/*.java",
+    ],
     static_libs: ["window-sidecar"],
     installable: true,
     sdk_version: "core_platform",
     system_ext_specific: true,
-    libs: ["framework", "androidx.annotation_annotation",],
-    required: ["androidx.window.sidecar.xml",],
+    libs: [
+        "framework",
+        "androidx.annotation_annotation",
+    ],
+    required: ["androidx.window.sidecar.xml"],
 }
 
 prebuilt_etc {
@@ -58,13 +65,20 @@
 
 java_library {
     name: "androidx.window.extensions",
-    srcs: ["src/androidx/window/extensions/**/*.java", "src/androidx/window/util/**/*.java"],
+    srcs: [
+        "src/androidx/window/extensions/**/*.java",
+        "src/androidx/window/util/**/*.java",
+        "src/androidx/window/common/**/*.java",
+    ],
     static_libs: ["window-extensions"],
     installable: true,
     sdk_version: "core_platform",
     system_ext_specific: true,
-    libs: ["framework", "androidx.annotation_annotation",],
-    required: ["androidx.window.extensions.xml",],
+    libs: [
+        "framework",
+        "androidx.annotation_annotation",
+    ],
+    required: ["androidx.window.extensions.xml"],
 }
 
 prebuilt_etc {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonDisplayFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonDisplayFeature.java
new file mode 100644
index 0000000..e6ad011
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonDisplayFeature.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2021 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 androidx.window.common;
+
+import static androidx.window.util.ExtensionHelper.isZero;
+
+import android.annotation.Nullable;
+import android.graphics.Rect;
+
+import androidx.annotation.NonNull;
+
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/** Wrapper for both Extension and Sidecar versions of DisplayFeature. */
+final class CommonDisplayFeature implements DisplayFeature {
+    private static final Pattern FEATURE_PATTERN =
+            Pattern.compile("([a-z]+)-\\[(\\d+),(\\d+),(\\d+),(\\d+)]");
+
+    private static final String FEATURE_TYPE_FOLD = "fold";
+    private static final String FEATURE_TYPE_HINGE = "hinge";
+
+    // TODO(b/183049815): Support feature strings that include the state of the feature.
+    /**
+     * Parses a display feature from a string.
+     *
+     * @throws IllegalArgumentException if the provided string is improperly formatted or could not
+     * otherwise be parsed.
+     *
+     * @see #FEATURE_PATTERN
+     */
+    @NonNull
+    static CommonDisplayFeature parseFromString(@NonNull String string) {
+        Matcher featureMatcher = FEATURE_PATTERN.matcher(string);
+        if (!featureMatcher.matches()) {
+            throw new IllegalArgumentException("Malformed feature description format: " + string);
+        }
+        try {
+            String featureType = featureMatcher.group(1);
+            int type;
+            switch (featureType) {
+                case FEATURE_TYPE_FOLD:
+                    type = 1 /* TYPE_FOLD */;
+                    break;
+                case FEATURE_TYPE_HINGE:
+                    type = 2 /* TYPE_HINGE */;
+                    break;
+                default: {
+                    throw new IllegalArgumentException("Malformed feature type: " + featureType);
+                }
+            }
+
+            int left = Integer.parseInt(featureMatcher.group(2));
+            int top = Integer.parseInt(featureMatcher.group(3));
+            int right = Integer.parseInt(featureMatcher.group(4));
+            int bottom = Integer.parseInt(featureMatcher.group(5));
+            Rect featureRect = new Rect(left, top, right, bottom);
+            if (isZero(featureRect)) {
+                throw new IllegalArgumentException("Feature has empty bounds: " + string);
+            }
+
+            return new CommonDisplayFeature(type, null, featureRect);
+        } catch (NumberFormatException e) {
+            throw new IllegalArgumentException("Malformed feature description: " + string, e);
+        }
+    }
+
+    private final int mType;
+    @Nullable
+    private final Integer mState;
+    @NonNull
+    private final Rect mRect;
+
+    CommonDisplayFeature(int type, @Nullable Integer state, @NonNull Rect rect) {
+        this.mType = type;
+        this.mState = state;
+        if (rect.width() == 0 && rect.height() == 0) {
+            throw new IllegalArgumentException(
+                    "Display feature rectangle cannot have zero width and height simultaneously.");
+        }
+        this.mRect = rect;
+    }
+
+    public int getType() {
+        return mType;
+    }
+
+    /** Returns the state of the feature, or {@code null} if the feature has no state. */
+    @Nullable
+    public Integer getState() {
+        return mState;
+    }
+
+    @NonNull
+    public Rect getRect() {
+        return mRect;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        CommonDisplayFeature that = (CommonDisplayFeature) o;
+        return mType == that.mType
+                && Objects.equals(mState, that.mState)
+                && mRect.equals(that.mRect);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mType, mState, mRect);
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerPostureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerPostureProducer.java
new file mode 100644
index 0000000..fa9a5a8
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerPostureProducer.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2021 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 androidx.window.common;
+
+import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.devicestate.DeviceStateManager;
+import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
+import android.util.Log;
+import android.util.SparseIntArray;
+
+import androidx.window.util.BaseDataProducer;
+
+import com.android.internal.R;
+
+import java.util.Optional;
+
+/**
+ * An implementation of {@link androidx.window.util.DataProducer} that returns the device's posture
+ * by mapping the state returned from {@link DeviceStateManager} to values provided in the resources
+ * config at {@link R.array#config_device_state_postures}.
+ */
+public final class DeviceStateManagerPostureProducer extends BaseDataProducer<Integer> {
+    private static final String TAG = "ConfigDevicePostureProducer";
+    private static final boolean DEBUG = false;
+
+    private final SparseIntArray mDeviceStateToPostureMap = new SparseIntArray();
+
+    private int mCurrentDeviceState = INVALID_DEVICE_STATE;
+
+    private final DeviceStateCallback mDeviceStateCallback = (state) -> {
+        mCurrentDeviceState = state;
+        notifyDataChanged();
+    };
+
+    public DeviceStateManagerPostureProducer(@NonNull Context context) {
+        String[] deviceStatePosturePairs = context.getResources()
+                .getStringArray(R.array.config_device_state_postures);
+        for (String deviceStatePosturePair : deviceStatePosturePairs) {
+            String[] deviceStatePostureMapping = deviceStatePosturePair.split(":");
+            if (deviceStatePostureMapping.length != 2) {
+                if (DEBUG) {
+                    Log.e(TAG, "Malformed device state posture pair: " + deviceStatePosturePair);
+                }
+                continue;
+            }
+
+            int deviceState;
+            int posture;
+            try {
+                deviceState = Integer.parseInt(deviceStatePostureMapping[0]);
+                posture = Integer.parseInt(deviceStatePostureMapping[1]);
+            } catch (NumberFormatException e) {
+                if (DEBUG) {
+                    Log.e(TAG, "Failed to parse device state or posture: " + deviceStatePosturePair,
+                            e);
+                }
+                continue;
+            }
+
+            mDeviceStateToPostureMap.put(deviceState, posture);
+        }
+
+        if (mDeviceStateToPostureMap.size() > 0) {
+            context.getSystemService(DeviceStateManager.class)
+                    .registerCallback(context.getMainExecutor(), mDeviceStateCallback);
+        }
+    }
+
+    @Override
+    @Nullable
+    public Optional<Integer> getData() {
+        final int posture = mDeviceStateToPostureMap.get(mCurrentDeviceState, -1);
+        return posture != -1 ? Optional.of(posture) : Optional.empty();
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DisplayFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DisplayFeature.java
new file mode 100644
index 0000000..b6c4c43
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DisplayFeature.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2021 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 androidx.window.common;
+
+import android.annotation.Nullable;
+import android.graphics.Rect;
+
+import androidx.annotation.NonNull;
+
+/** Wrapper for both Extension and Sidecar versions of DisplayFeature. */
+public interface DisplayFeature {
+    /** Returns the type of the feature. */
+    int getType();
+
+    /** Returns the state of the feature, or {@code null} if the feature has no state. */
+    @Nullable
+    Integer getState();
+
+    /** Returns the bounds of the feature. */
+    @NonNull
+    Rect getRect();
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/ResourceConfigDisplayFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/ResourceConfigDisplayFeatureProducer.java
new file mode 100644
index 0000000..cd2cadc
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/ResourceConfigDisplayFeatureProducer.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 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 androidx.window.common;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.window.util.BaseDataProducer;
+
+import com.android.internal.R;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Implementation of {@link androidx.window.util.DataProducer} that produces
+ * {@link CommonDisplayFeature} parsed from a string stored in the resources config at
+ * {@link R.string#config_display_features}.
+ */
+public final class ResourceConfigDisplayFeatureProducer extends
+        BaseDataProducer<List<DisplayFeature>> {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "ResourceConfigDisplayFeatureProducer";
+
+    private final Context mContext;
+
+    public ResourceConfigDisplayFeatureProducer(@NonNull Context context) {
+        mContext = context;
+    }
+
+    @Override
+    @Nullable
+    public Optional<List<DisplayFeature>> getData() {
+        String displayFeaturesString = mContext.getResources().getString(
+                R.string.config_display_features);
+        if (TextUtils.isEmpty(displayFeaturesString)) {
+            return Optional.empty();
+        }
+
+        List<DisplayFeature> features = new ArrayList<>();
+        String[] featureStrings =  displayFeaturesString.split(";");
+        for (String featureString : featureStrings) {
+            CommonDisplayFeature feature;
+            try {
+                feature = CommonDisplayFeature.parseFromString(featureString);
+            } catch (IllegalArgumentException e) {
+                if (DEBUG) {
+                    Log.w(TAG, "Failed to parse display feature: " + featureString, e);
+                }
+                continue;
+            }
+            features.add(feature);
+        }
+        return Optional.of(features);
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDevicePostureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDevicePostureProducer.java
new file mode 100644
index 0000000..2026df3
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDevicePostureProducer.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2021 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 androidx.window.common;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+
+import androidx.window.util.BaseDataProducer;
+
+import java.util.Optional;
+
+/**
+ * Implementation of {@link androidx.window.util.DataProducer} that provides the device posture
+ * as an {@link Integer} from a value stored in {@link Settings}.
+ */
+public final class SettingsDevicePostureProducer extends BaseDataProducer<Integer> {
+    private static final String DEVICE_POSTURE = "device_posture";
+
+    private final Uri mDevicePostureUri =
+            Settings.Global.getUriFor(DEVICE_POSTURE);
+
+    private final ContentResolver mResolver;
+    private final ContentObserver mObserver;
+    private boolean mRegisteredObservers;
+
+    public SettingsDevicePostureProducer(@NonNull Context context) {
+        mResolver = context.getContentResolver();
+        mObserver = new SettingsObserver();
+    }
+
+    @Override
+    @Nullable
+    public Optional<Integer> getData() {
+        int posture = Settings.Global.getInt(mResolver, DEVICE_POSTURE, -1);
+        return posture == -1 ? Optional.empty() : Optional.of(posture);
+    }
+
+    /**
+     * Registers settings observers, if needed. When settings observers are registered for this
+     * producer callbacks for changes in data will be triggered.
+     */
+    public void registerObserversIfNeeded() {
+        if (mRegisteredObservers) {
+            return;
+        }
+        mRegisteredObservers = true;
+        mResolver.registerContentObserver(mDevicePostureUri, false /* notifyForDescendants */,
+                mObserver /* ContentObserver */);
+    }
+
+    /**
+     * Unregisters settings observers, if needed. When settings observers are unregistered for this
+     * producer callbacks for changes in data will not be triggered.
+     */
+    public void unregisterObserversIfNeeded() {
+        if (!mRegisteredObservers) {
+            return;
+        }
+        mRegisteredObservers = false;
+        mResolver.unregisterContentObserver(mObserver);
+    }
+
+    private final class SettingsObserver extends ContentObserver {
+        SettingsObserver() {
+            super(new Handler(Looper.getMainLooper()));
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            if (mDevicePostureUri.equals(uri)) {
+                notifyDataChanged();
+            }
+        }
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDisplayFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDisplayFeatureProducer.java
new file mode 100644
index 0000000..0406626
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDisplayFeatureProducer.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2021 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 androidx.window.common;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.window.util.BaseDataProducer;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Implementation of {@link androidx.window.util.DataProducer} that produces
+ * {@link CommonDisplayFeature} parsed from a string stored in {@link Settings}.
+ */
+public final class SettingsDisplayFeatureProducer
+        extends BaseDataProducer<List<DisplayFeature>> {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "SettingsDisplayFeatureProducer";
+    private static final String DISPLAY_FEATURES = "display_features";
+
+    private final Uri mDisplayFeaturesUri =
+            Settings.Global.getUriFor(DISPLAY_FEATURES);
+
+    private final ContentResolver mResolver;
+    private final ContentObserver mObserver;
+    private boolean mRegisteredObservers;
+
+    public SettingsDisplayFeatureProducer(@NonNull Context context) {
+        mResolver = context.getContentResolver();
+        mObserver = new SettingsObserver();
+    }
+
+    @Override
+    @Nullable
+    public Optional<List<DisplayFeature>> getData() {
+        String displayFeaturesString = Settings.Global.getString(mResolver, DISPLAY_FEATURES);
+        if (displayFeaturesString == null) {
+            return Optional.empty();
+        }
+
+        List<DisplayFeature> features = new ArrayList<>();
+        if (TextUtils.isEmpty(displayFeaturesString)) {
+            return Optional.of(features);
+        }
+        String[] featureStrings =  displayFeaturesString.split(";");
+
+        for (String featureString : featureStrings) {
+            CommonDisplayFeature feature;
+            try {
+                feature = CommonDisplayFeature.parseFromString(featureString);
+            } catch (IllegalArgumentException e) {
+                if (DEBUG) {
+                    Log.w(TAG, "Failed to parse display feature: " + featureString, e);
+                }
+                continue;
+            }
+            features.add(feature);
+        }
+        return Optional.of(features);
+    }
+
+    /**
+     * Registers settings observers, if needed. When settings observers are registered for this
+     * producer callbacks for changes in data will be triggered.
+     */
+    public void registerObserversIfNeeded() {
+        if (mRegisteredObservers) {
+            return;
+        }
+        mRegisteredObservers = true;
+        mResolver.registerContentObserver(mDisplayFeaturesUri, false /* notifyForDescendants */,
+                mObserver /* ContentObserver */);
+    }
+
+    /**
+     * Unregisters settings observers, if needed. When settings observers are unregistered for this
+     * producer callbacks for changes in data will not be triggered.
+     */
+    public void unregisterObserversIfNeeded() {
+        if (!mRegisteredObservers) {
+            return;
+        }
+        mRegisteredObservers = false;
+        mResolver.unregisterContentObserver(mObserver);
+    }
+
+    private final class SettingsObserver extends ContentObserver {
+        SettingsObserver() {
+            super(new Handler(Looper.getMainLooper()));
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            if (mDisplayFeaturesUri.equals(uri)) {
+                notifyDataChanged();
+            }
+        }
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
index 5c91cf41..ce9be6a 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
@@ -27,11 +27,17 @@
 import android.util.Log;
 
 import androidx.annotation.NonNull;
-import androidx.window.util.BaseDisplayFeature;
-import androidx.window.util.SettingsConfigProvider;
+import androidx.window.common.DeviceStateManagerPostureProducer;
+import androidx.window.common.DisplayFeature;
+import androidx.window.common.ResourceConfigDisplayFeatureProducer;
+import androidx.window.common.SettingsDevicePostureProducer;
+import androidx.window.common.SettingsDisplayFeatureProducer;
+import androidx.window.util.DataProducer;
+import androidx.window.util.PriorityDataProducer;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 
 /**
  * Reference implementation of androidx.window.extensions OEM interface for use with
@@ -41,23 +47,46 @@
  * production builds since the interface can still change before reaching stable version.
  * Please refer to {@link androidx.window.sidecar.SampleSidecarImpl} instead.
  */
-class SampleExtensionImpl extends StubExtension implements
-        SettingsConfigProvider.StateChangeCallback {
+class SampleExtensionImpl extends StubExtension {
     private static final String TAG = "SampleExtension";
 
-    private final SettingsConfigProvider mConfigProvider;
+    private final SettingsDevicePostureProducer mSettingsDevicePostureProducer;
+    private final DataProducer<Integer> mDevicePostureProducer;
+
+    private final SettingsDisplayFeatureProducer mSettingsDisplayFeatureProducer;
+    private final DataProducer<List<DisplayFeature>> mDisplayFeatureProducer;
 
     SampleExtensionImpl(Context context) {
-        mConfigProvider = new SettingsConfigProvider(context, this);
+        mSettingsDevicePostureProducer = new SettingsDevicePostureProducer(context);
+        mDevicePostureProducer = new PriorityDataProducer<>(List.of(
+                mSettingsDevicePostureProducer,
+                new DeviceStateManagerPostureProducer(context)
+        ));
+
+        mSettingsDisplayFeatureProducer = new SettingsDisplayFeatureProducer(context);
+        mDisplayFeatureProducer = new PriorityDataProducer<>(List.of(
+                mSettingsDisplayFeatureProducer,
+                new ResourceConfigDisplayFeatureProducer(context)
+        ));
+
+        mDevicePostureProducer.addDataChangedCallback(this::onDevicePostureChanged);
+        mDisplayFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
     }
 
-    @Override
-    public void onDevicePostureChanged() {
-        updateDeviceState(new ExtensionDeviceState(mConfigProvider.getDeviceState()));
+    private void onDevicePostureChanged() {
+        updateDeviceState(new ExtensionDeviceState(getDevicePosture()));
+
+        // Trigger a change in display features as the posture will be used in place of the feature
+        // state if the state is left unset by the producer.
+        onDisplayFeaturesChanged();
     }
 
-    @Override
-    public void onDisplayFeaturesChanged() {
+    private int getDevicePosture() {
+        Optional<Integer> posture = mDevicePostureProducer.getData();
+        return posture.orElse(ExtensionDeviceState.POSTURE_UNKNOWN);
+    }
+
+    private void onDisplayFeaturesChanged() {
         for (Activity activity : getActivitiesListeningForLayoutChanges()) {
             ExtensionWindowLayoutInfo newLayout = getWindowLayoutInfo(activity);
             updateWindowLayout(activity, newLayout);
@@ -84,13 +113,20 @@
             return features;
         }
 
-        List<BaseDisplayFeature> storedFeatures = mConfigProvider.getDisplayFeatures();
-        for (BaseDisplayFeature baseFeature : storedFeatures) {
-            Rect featureRect = baseFeature.getRect();
-            rotateRectToDisplayRotation(displayId, featureRect);
-            transformToWindowSpaceRect(activity, featureRect);
-            features.add(new ExtensionFoldingFeature(featureRect, baseFeature.getType(),
-                    baseFeature.getState()));
+        Optional<List<DisplayFeature>> storedFeatures = mDisplayFeatureProducer.getData();
+        if (storedFeatures.isPresent()) {
+            int posture = getDevicePosture();
+
+            for (DisplayFeature baseFeature : storedFeatures.get()) {
+                Rect featureRect = baseFeature.getRect();
+                rotateRectToDisplayRotation(displayId, featureRect);
+                transformToWindowSpaceRect(activity, featureRect);
+
+                Integer featureState = baseFeature.getState();
+
+                features.add(new ExtensionFoldingFeature(featureRect, baseFeature.getType(),
+                        featureState == null ? posture : featureState));
+            }
         }
         return features;
     }
@@ -98,9 +134,11 @@
     @Override
     protected void onListenersChanged() {
         if (hasListeners()) {
-            mConfigProvider.registerObserversIfNeeded();
+            mSettingsDevicePostureProducer.registerObserversIfNeeded();
+            mSettingsDisplayFeatureProducer.registerObserversIfNeeded();
         } else {
-            mConfigProvider.unregisterObserversIfNeeded();
+            mSettingsDevicePostureProducer.unregisterObserversIfNeeded();
+            mSettingsDisplayFeatureProducer.unregisterObserversIfNeeded();
         }
 
         onDevicePostureChanged();
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
index d3700f8..ece198c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
@@ -29,33 +29,53 @@
 import android.util.Log;
 
 import androidx.annotation.NonNull;
-import androidx.window.util.BaseDisplayFeature;
-import androidx.window.util.SettingsConfigProvider;
+import androidx.window.common.DeviceStateManagerPostureProducer;
+import androidx.window.common.DisplayFeature;
+import androidx.window.common.ResourceConfigDisplayFeatureProducer;
+import androidx.window.common.SettingsDevicePostureProducer;
+import androidx.window.common.SettingsDisplayFeatureProducer;
+import androidx.window.util.DataProducer;
+import androidx.window.util.PriorityDataProducer;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 
 /**
  * Reference implementation of androidx.window.sidecar OEM interface for use with
  * WindowManager Jetpack.
  */
-class SampleSidecarImpl extends StubSidecar implements
-        SettingsConfigProvider.StateChangeCallback {
+class SampleSidecarImpl extends StubSidecar {
     private static final String TAG = "SampleSidecar";
 
-    private final SettingsConfigProvider mConfigProvider;
+    private final SettingsDevicePostureProducer mSettingsDevicePostureProducer;
+    private final DataProducer<Integer> mDevicePostureProducer;
+
+    private final SettingsDisplayFeatureProducer mSettingsDisplayFeatureProducer;
+    private final DataProducer<List<DisplayFeature>> mDisplayFeatureProducer;
 
     SampleSidecarImpl(Context context) {
-        mConfigProvider = new SettingsConfigProvider(context, this);
+        mSettingsDevicePostureProducer = new SettingsDevicePostureProducer(context);
+        mDevicePostureProducer = new PriorityDataProducer<>(List.of(
+                mSettingsDevicePostureProducer,
+                new DeviceStateManagerPostureProducer(context)
+        ));
+
+        mSettingsDisplayFeatureProducer = new SettingsDisplayFeatureProducer(context);
+        mDisplayFeatureProducer = new PriorityDataProducer<>(List.of(
+                mSettingsDisplayFeatureProducer,
+                new ResourceConfigDisplayFeatureProducer(context)
+        ));
+
+        mDevicePostureProducer.addDataChangedCallback(this::onDevicePostureChanged);
+        mDisplayFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
     }
 
-    @Override
-    public void onDevicePostureChanged() {
+    private void onDevicePostureChanged() {
         updateDeviceState(getDeviceState());
     }
 
-    @Override
-    public void onDisplayFeaturesChanged() {
+    private void onDisplayFeaturesChanged() {
         for (IBinder windowToken : getWindowsListeningForLayoutChanges()) {
             SidecarWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken);
             updateWindowLayout(windowToken, newLayout);
@@ -65,8 +85,10 @@
     @NonNull
     @Override
     public SidecarDeviceState getDeviceState() {
+        Optional<Integer> posture = mDevicePostureProducer.getData();
+
         SidecarDeviceState deviceState = new SidecarDeviceState();
-        deviceState.posture = mConfigProvider.getDeviceState();
+        deviceState.posture = posture.orElse(SidecarDeviceState.POSTURE_UNKNOWN);
         return deviceState;
     }
 
@@ -96,15 +118,17 @@
             return features;
         }
 
-        List<BaseDisplayFeature> storedFeatures = mConfigProvider.getDisplayFeatures();
-        for (BaseDisplayFeature baseFeature : storedFeatures) {
-            SidecarDisplayFeature feature = new SidecarDisplayFeature();
-            Rect featureRect = baseFeature.getRect();
-            rotateRectToDisplayRotation(displayId, featureRect);
-            transformToWindowSpaceRect(activity, featureRect);
-            feature.setRect(featureRect);
-            feature.setType(baseFeature.getType());
-            features.add(feature);
+        Optional<List<DisplayFeature>> storedFeatures = mDisplayFeatureProducer.getData();
+        if (storedFeatures.isPresent()) {
+            for (DisplayFeature baseFeature : storedFeatures.get()) {
+                SidecarDisplayFeature feature = new SidecarDisplayFeature();
+                Rect featureRect = baseFeature.getRect();
+                rotateRectToDisplayRotation(displayId, featureRect);
+                transformToWindowSpaceRect(activity, featureRect);
+                feature.setRect(featureRect);
+                feature.setType(baseFeature.getType());
+                features.add(feature);
+            }
         }
         return features;
     }
@@ -112,9 +136,11 @@
     @Override
     protected void onListenersChanged() {
         if (hasListeners()) {
-            mConfigProvider.registerObserversIfNeeded();
+            mSettingsDevicePostureProducer.registerObserversIfNeeded();
+            mSettingsDisplayFeatureProducer.registerObserversIfNeeded();
         } else {
-            mConfigProvider.unregisterObserversIfNeeded();
+            mSettingsDevicePostureProducer.unregisterObserversIfNeeded();
+            mSettingsDisplayFeatureProducer.unregisterObserversIfNeeded();
         }
     }
 }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java
new file mode 100644
index 0000000..0a46703451
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 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 androidx.window.util;
+
+import androidx.annotation.NonNull;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * Base class that provides the implementation for the callback mechanism of the
+ * {@link DataProducer} API.
+ *
+ * @param <T> The type of data this producer returns through {@link #getData()}.
+ */
+public abstract class BaseDataProducer<T> implements DataProducer<T> {
+    private final Set<Runnable> mCallbacks = new LinkedHashSet<>();
+
+    @Override
+    public final void addDataChangedCallback(@NonNull Runnable callback) {
+        mCallbacks.add(callback);
+    }
+
+    @Override
+    public final void removeDataChangedCallback(@NonNull Runnable callback) {
+        mCallbacks.remove(callback);
+    }
+
+    /**
+     * Called to notify all registered callbacks that the data provided by {@link #getData()} has
+     * changed.
+     */
+    protected void notifyDataChanged() {
+        for (Runnable callback : mCallbacks) {
+            callback.run();
+        }
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDisplayFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDisplayFeature.java
deleted file mode 100644
index b74a2a4..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDisplayFeature.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2021 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 androidx.window.util;
-
-import android.graphics.Rect;
-
-import androidx.annotation.NonNull;
-
-/** Wrapper for both Extension and Sidecar versions of DisplayFeature. */
-public class BaseDisplayFeature {
-    private final int mType;
-    private final int mState;
-    @NonNull
-    public final Rect mRect;
-
-    public BaseDisplayFeature(int type, int state, @NonNull Rect rect) {
-        this.mType = type;
-        this.mState = state;
-        if (rect.width() == 0 && rect.height() == 0) {
-            throw new IllegalArgumentException(
-                    "Display feature rectangle cannot have zero width and height simultaneously.");
-        }
-        this.mRect = rect;
-    }
-
-    public int getType() {
-        return mType;
-    }
-
-    public int getState() {
-        return mState;
-    }
-
-    @NonNull
-    public Rect getRect() {
-        return mRect;
-    }
-}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/DataProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/util/DataProducer.java
new file mode 100644
index 0000000..d4d1a23
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/DataProducer.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2021 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 androidx.window.util;
+
+import android.annotation.NonNull;
+
+import java.util.Optional;
+
+/**
+ * Produces data through {@link #getData()} and provides a mechanism for receiving a callback when
+ * the data managed by the produces has changed.
+ *
+ * @param <T> The type of data this producer returns through {@link #getData()}.
+ */
+public interface DataProducer<T> {
+    /**
+     * Returns the data currently stored in the provider, or {@link Optional#empty()} if the
+     * provider has no data.
+     */
+    Optional<T> getData();
+
+    /**
+     * Adds a callback to be notified when the data returned from {@link #getData()} has changed.
+     */
+    void addDataChangedCallback(@NonNull Runnable callback);
+
+    /** Removes a callback previously added with {@link #addDataChangedCallback(Runnable)}.  */
+    void removeDataChangedCallback(@NonNull Runnable callback);
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/PriorityDataProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/util/PriorityDataProducer.java
new file mode 100644
index 0000000..990ae20
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/PriorityDataProducer.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2021 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 androidx.window.util;
+
+import android.annotation.Nullable;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Implementation of {@link DataProducer} that delegates calls to {@link #getData()} to the list of
+ * provided child producers.
+ * <p>
+ * The value returned is based on the precedence of the supplied children where the producer with
+ * index 0 has a higher precedence than producers that come later in the list. When a producer with
+ * a higher precedence has a non-empty value returned from {@link #getData()}, its value will be
+ * returned from an instance of this class, ignoring all other producers with lower precedence.
+ *
+ * @param <T> The type of data this producer returns through {@link #getData()}.
+ */
+public final class PriorityDataProducer<T> extends BaseDataProducer<T> {
+    private final List<DataProducer<T>> mChildProducers;
+
+    public PriorityDataProducer(List<DataProducer<T>> childProducers) {
+        mChildProducers = childProducers;
+        for (DataProducer<T> childProducer : mChildProducers) {
+            childProducer.addDataChangedCallback(this::notifyDataChanged);
+        }
+    }
+
+    @Nullable
+    @Override
+    public Optional<T> getData() {
+        for (DataProducer<T> childProducer : mChildProducers) {
+            final Optional<T> data = childProducer.getData();
+            if (data.isPresent()) {
+                return data;
+            }
+        }
+        return Optional.empty();
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/SettingsConfigProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/util/SettingsConfigProvider.java
deleted file mode 100644
index 6dd190c..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/util/SettingsConfigProvider.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (C) 2021 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 androidx.window.util;
-
-import static androidx.window.util.ExtensionHelper.isZero;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.graphics.Rect;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import com.android.internal.R;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Device and display feature state provider that uses Settings as the source.
- */
-public final class SettingsConfigProvider extends ContentObserver {
-    private static final String TAG = "SettingsConfigProvider";
-    private static final String DEVICE_POSTURE = "device_posture";
-    private static final String DISPLAY_FEATURES = "display_features";
-
-    private static final Pattern FEATURE_PATTERN =
-            Pattern.compile("([a-z]+)-\\[(\\d+),(\\d+),(\\d+),(\\d+)]");
-
-    private static final String FEATURE_TYPE_FOLD = "fold";
-    private static final String FEATURE_TYPE_HINGE = "hinge";
-
-    private final Uri mDevicePostureUri =
-            Settings.Global.getUriFor(DEVICE_POSTURE);
-    private final Uri mDisplayFeaturesUri =
-            Settings.Global.getUriFor(DISPLAY_FEATURES);
-    private final Context mContext;
-    private final ContentResolver mResolver;
-    private final StateChangeCallback mCallback;
-    private boolean mRegisteredObservers;
-
-    public SettingsConfigProvider(@NonNull Context context, @NonNull StateChangeCallback callback) {
-        super(new Handler(Looper.getMainLooper()));
-        mContext = context;
-        mResolver = context.getContentResolver();
-        mCallback = callback;
-    }
-
-    /**
-     * Registers the content observers for Settings keys that store device state and display feature
-     * configurations.
-     */
-    public void registerObserversIfNeeded() {
-        if (mRegisteredObservers) {
-            return;
-        }
-        mRegisteredObservers = true;
-        mResolver.registerContentObserver(mDevicePostureUri, false /* notifyForDescendants */,
-                this /* ContentObserver */);
-        mResolver.registerContentObserver(mDisplayFeaturesUri, false /* notifyForDescendants */,
-                this /* ContentObserver */);
-    }
-
-    /**
-     * Unregisters the content observers that are tracking the state changes.
-     * @see #registerObserversIfNeeded()
-     */
-    public void unregisterObserversIfNeeded() {
-        if (!mRegisteredObservers) {
-            return;
-        }
-        mRegisteredObservers = false;
-        mResolver.unregisterContentObserver(this);
-    }
-
-    /**
-     * Gets the device posture int stored in Settings.
-     */
-    public int getDeviceState() {
-        return Settings.Global.getInt(mResolver, DEVICE_POSTURE,
-                0 /* POSTURE_UNKNOWN */);
-    }
-
-    /**
-     * Gets the list of all display feature configs stored in Settings. Uses a custom
-     * {@link BaseDisplayFeature} class to report the config to be translated for actual
-     * containers in Sidecar or Extensions.
-     */
-    public List<BaseDisplayFeature> getDisplayFeatures() {
-        List<BaseDisplayFeature> features = new ArrayList<>();
-        String displayFeaturesString = Settings.Global.getString(mResolver, DISPLAY_FEATURES);
-        if (TextUtils.isEmpty(displayFeaturesString)) {
-            displayFeaturesString = mContext.getResources().getString(
-                    R.string.config_display_features);
-        }
-        if (TextUtils.isEmpty(displayFeaturesString)) {
-            return features;
-        }
-        String[] featureStrings =  displayFeaturesString.split(";");
-
-        int deviceState = getDeviceState();
-
-        for (String featureString : featureStrings) {
-            Matcher featureMatcher = FEATURE_PATTERN.matcher(featureString);
-            if (!featureMatcher.matches()) {
-                Log.e(TAG, "Malformed feature description format: " + featureString);
-                continue;
-            }
-            try {
-                String featureType = featureMatcher.group(1);
-                int type;
-                switch (featureType) {
-                    case FEATURE_TYPE_FOLD:
-                        type = 1 /* TYPE_FOLD */;
-                        break;
-                    case FEATURE_TYPE_HINGE:
-                        type = 2 /* TYPE_HINGE */;
-                        break;
-                    default: {
-                        Log.e(TAG, "Malformed feature type: " + featureType);
-                        continue;
-                    }
-                }
-
-                int left = Integer.parseInt(featureMatcher.group(2));
-                int top = Integer.parseInt(featureMatcher.group(3));
-                int right = Integer.parseInt(featureMatcher.group(4));
-                int bottom = Integer.parseInt(featureMatcher.group(5));
-                Rect featureRect = new Rect(left, top, right, bottom);
-                if (!isZero(featureRect)) {
-                    BaseDisplayFeature feature = new BaseDisplayFeature(type, deviceState,
-                            featureRect);
-                    features.add(feature);
-                } else {
-                    Log.w(TAG, "Read empty feature");
-                }
-            } catch (NumberFormatException e) {
-                Log.e(TAG, "Malformed feature description: " + featureString);
-            }
-        }
-        return features;
-    }
-
-    @Override
-    public void onChange(boolean selfChange, Uri uri) {
-        if (uri == null) {
-            return;
-        }
-
-        if (mDevicePostureUri.equals(uri)) {
-            mCallback.onDevicePostureChanged();
-            mCallback.onDisplayFeaturesChanged();
-            return;
-        }
-        if (mDisplayFeaturesUri.equals(uri)) {
-            mCallback.onDisplayFeaturesChanged();
-        }
-    }
-
-    /**
-     * Callback that notifies about device or display feature state changes.
-     */
-    public interface StateChangeCallback {
-        /**
-         * Notifies about the device state update.
-         */
-        void onDevicePostureChanged();
-
-        /**
-         * Notifies about the display feature config update.
-         */
-        void onDisplayFeaturesChanged();
-    }
-}