Merge "Use FakeFeatureFactory in SafetySourceBroadcastReceiverTest" into tm-dev
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 9d868cb..917c666 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -13731,18 +13731,26 @@
     translate "maximum balance when device is fully charged" instead. Balance is the same meaning as
     having money in a bank account. Balance in our feature is the amount of Android Resource Credits
     an app can have. Android Resource Credits are a form of payment used by apps to be able to
-    perform tasks. [CHAR LIMIT=40]-->
+    perform tasks. [CHAR LIMIT=80]-->
     <string name="tare_max_satiated_balance">Maximum Satiated Balance</string>
-    <!-- Title for the TARE policy factor that determines the maximum credits in circulation between
-    all the apps [CHAR LIMIT=40]-->
-    <string name="tare_max_circulation">Maximum Circulation</string>
-    <!-- Title for the TARE policy factor that determines the minimum credits an app can have in one
-    battery life cycle. Satiated means battery is fully charged; If this is not easily translatable,
-    translate "minimum balance when device is fully charged" instead. Balance is the same meaning as
-    having money in a bank account. Balance in our feature is the amount of Android Resource Credits
-    an app can have. Android Resource Credits are a form of payment used by apps to be able to
-    perform tasks. [CHAR LIMIT=40]-->
-    <string name="tare_min_satiated_balance">Minimum Satiated Balance</string>
+    <!-- Title for the TARE policy factors that affect how many credits an app may have. Balance
+    in this context is the same as "bank balance" or "account balance" (ie. how much "money" may be
+    in a bank account). [CHAR LIMIT=55]-->
+    <string name="tare_balances">Balances</string>
+    <!-- Title for the TARE section to modify consumption limits. "Consumption" refers to the idea
+     using resources that are not replenished. [CHAR LIMIT=55]-->
+    <string name="tare_consumption_limits">Consumption Limits</string>
+    <!-- Title for the TARE policy factor that determines the initial maximum amount of credits that
+    can be consumed by all the apps [CHAR LIMIT=80]-->
+    <string name="tare_initial_consumption_limit">Initial Consumption Limit</string>
+    <!-- Title for the TARE policy factor that determines the maximum consumption limit the system
+     can have [CHAR LIMIT=80]-->
+    <string name="tare_hard_consumption_limit">Maximum Consumption Limit</string>
+    <!-- Titles for the consumption limits factors. [CHAR LIMIT=40]-->
+    <string-array name="tare_consumption_limit_subfactors" translatable="false">
+        <item>@string/tare_initial_consumption_limit</item>
+        <item>@string/tare_hard_consumption_limit</item>
+    </string-array>
     <!-- Title for the various modifiers that alter the cost of TARE tasks based on battery status
     (charging, power save mode, etc.) [CHAR LIMIT=40]-->
     <string name="tare_modifiers">Modifiers</string>
@@ -13778,14 +13786,6 @@
     <string name="tare_nonwakeup_inexact" translatable="false">Inexact NonWakeup Alarm</string>
     <!-- Title for the AlarmClock alarm set via AlarmManager.setAlarmClock() [CHAR LIMIT=50]-->
     <string name="tare_alarm_clock" translatable="false">AlarmClock</string>
-    <!-- Exempted apps are those apps exempted from most power saving features. [CHAR LIMIT=50]-->
-    <string name="tare_exempted">Exempted</string>
-    <!-- A headless system app is a preinstalled app that does not have any activities/UI that the
-    user can interact with. [CHAR LIMIT=50]-->
-    <string name="tare_headless_app">Headless System App</string>
-    <!-- Other apps are those apps interacted with by users that are not exempted or headless
-    system apps. [CHAR LIMIT=50]-->
-    <string name="tare_other_app">Other App</string>
     <!-- Top activity means an app is in the TOP android process state and is thus visible to the
     user[CHAR LIMIT=50]-->
     <string name="tare_top_activity">Top Activity</string>
@@ -13842,12 +13842,29 @@
     <!-- Title for the penalty an app receives for letting a job use the maximum execution time and
      time out [CHAR LIMIT=50]-->
     <string name="tare_job_timeout_penalty">Job Timeout Penalty</string>
+    <!-- Title for the TARE factor that determines the minimum credits an app should have when the
+    device is fully charged. "Satiated" in this context means that the battery is fully charged.
+    Balance is the same meaning as having money in a bank account.
+    Exempted apps are those apps exempted from most power saving features. [CHAR LIMIT=80]-->
+    <string name="tare_min_balance_exempted">Minimum Satiated Balance (Exempted)</string>
+    <!-- Title for the TARE factor that determines the minimum credits an app should have when the
+    device is fully charged. "Satiated" in this context means that the battery is fully charged.
+    Balance is the same meaning as having money in a bank account.
+    A headless system app is a preinstalled app that does not have any activities/UI that the
+    user can interact with. [CHAR LIMIT=80]-->
+    <string name="tare_min_balance_headless_app">Minimum Satiated Balance (Headless System App)</string>
+    <!-- Title for the TARE factor that determines the minimum credits an app should have when the
+    device is fully charged. "Satiated" in this context means that the battery is fully charged.
+    Balance is the same meaning as having money in a bank account.
+    Remaining apps are those apps that don't fit into predefined categories. [CHAR LIMIT=80]-->
+    <string name="tare_min_balance_other_app">Minimum Satiated Balance (Remaining Apps)</string>
     <!-- Titles for the minimum satiated credit balances for different types of apps
     (per battery cycle). Satiated means battery is fully charged. [CHAR LIMIT=40]-->
-    <string-array name="tare_min_satiated_balance_subfactors" translatable="false">
-        <item>@string/tare_exempted</item>
-        <item>@string/tare_headless_app</item>
-        <item>@string/tare_other_app</item>
+    <string-array name="tare_app_balance_subfactors" translatable="false">
+        <item>@string/tare_max_satiated_balance</item>
+        <item>@string/tare_min_balance_exempted</item>
+        <item>@string/tare_min_balance_headless_app</item>
+        <item>@string/tare_min_balance_other_app</item>
     </string-array>
     <!-- Various modifier subfactors that alter the cost of TARE tasks depending on what battery
     state the device is in [CHAR LIMIT=50]-->
diff --git a/src/com/android/settings/TetherSettings.java b/src/com/android/settings/TetherSettings.java
index e485d1e..ae24168 100644
--- a/src/com/android/settings/TetherSettings.java
+++ b/src/com/android/settings/TetherSettings.java
@@ -34,6 +34,9 @@
 import android.hardware.usb.UsbManager;
 import android.net.ConnectivityManager;
 import android.net.EthernetManager;
+import android.net.EthernetManager.InterfaceState;
+import android.net.EthernetManager.Role;
+import android.net.IpConfiguration;
 import android.net.TetheringManager;
 import android.net.wifi.WifiManager;
 import android.os.Bundle;
@@ -42,10 +45,10 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.SearchIndexableResource;
-import android.text.TextUtils;
 import android.util.FeatureFlagUtils;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
 import androidx.preference.SwitchPreference;
@@ -62,6 +65,7 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -97,7 +101,6 @@
     private BroadcastReceiver mTetherChangeReceiver;
 
     private String[] mBluetoothRegexs;
-    private String mEthernetRegex;
     private AtomicReference<BluetoothPan> mBluetoothPan = new AtomicReference<>();
 
     private Handler mHandler = new Handler();
@@ -106,6 +109,7 @@
     private EthernetManager mEm;
     private TetheringEventCallback mTetheringEventCallback;
     private EthernetListener mEthernetListener;
+    private final HashSet<String> mAvailableInterfaces = new HashSet<>();
 
     private WifiTetherPreferenceController mWifiTetherPreferenceController;
 
@@ -172,17 +176,17 @@
         mDataSaverBackend.addListener(this);
 
         mCm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
-        mEm = (EthernetManager) getSystemService(Context.ETHERNET_SERVICE);
         mTm = (TetheringManager) getSystemService(Context.TETHERING_SERVICE);
+        // Some devices do not have available EthernetManager. In that case getSystemService will
+        // return null.
+        mEm = mContext.getSystemService(EthernetManager.class);
 
         mUsbRegexs = mTm.getTetherableUsbRegexs();
         mBluetoothRegexs = mTm.getTetherableBluetoothRegexs();
-        mEthernetRegex = mContext.getResources().getString(
-                com.android.internal.R.string.config_ethernet_iface_regex);
 
         final boolean usbAvailable = mUsbRegexs.length != 0;
         final boolean bluetoothAvailable = adapter != null && mBluetoothRegexs.length != 0;
-        final boolean ethernetAvailable = !TextUtils.isEmpty(mEthernetRegex);
+        final boolean ethernetAvailable = (mEm != null);
 
         if (!usbAvailable || Utils.isMonkeyRunning()) {
             getPreferenceScreen().removePreference(mUsbTether);
@@ -330,7 +334,7 @@
 
         mEthernetListener = new EthernetListener();
         if (mEm != null)
-            mEm.addListener(mEthernetListener, r -> mHandler.post(r));
+            mEm.addInterfaceStateListener(r -> mHandler.post(r), mEthernetListener);
 
         updateUsbState();
         updateBluetoothAndEthernetState();
@@ -346,11 +350,10 @@
         getActivity().unregisterReceiver(mTetherChangeReceiver);
         mTm.unregisterTetheringEventCallback(mTetheringEventCallback);
         if (mEm != null)
-            mEm.removeListener(mEthernetListener);
+            mEm.removeInterfaceStateListener(mEthernetListener);
         mTetherChangeReceiver = null;
         mStartTetheringCallback = null;
         mTetheringEventCallback = null;
-        mEthernetListener = null;
     }
 
     @VisibleForTesting
@@ -483,11 +486,11 @@
         boolean isTethered = false;
 
         for (String s : available) {
-            if (s.matches(mEthernetRegex)) isAvailable = true;
+            if (mAvailableInterfaces.contains(s)) isAvailable = true;
         }
 
         for (String s : tethered) {
-            if (s.matches(mEthernetRegex)) isTethered = true;
+            if (mAvailableInterfaces.contains(s)) isTethered = true;
         }
 
         if (DEBUG) {
@@ -498,7 +501,7 @@
         if (isTethered) {
             mEthernetTether.setEnabled(!mDataSaverEnabled);
             mEthernetTether.setChecked(true);
-        } else if (isAvailable || (mEm != null && mEm.isAvailable())) {
+        } else if (mAvailableInterfaces.size() > 0) {
             mEthernetTether.setEnabled(!mDataSaverEnabled);
             mEthernetTether.setChecked(false);
         } else {
@@ -600,9 +603,9 @@
                         keys.add(KEY_ENABLE_BLUETOOTH_TETHERING);
                     }
 
-                    final boolean ethernetAvailable = !TextUtils.isEmpty(
-                            context.getResources().getString(
-                                    com.android.internal.R.string.config_ethernet_iface_regex));
+                    final EthernetManager em =
+                            context.getSystemService(EthernetManager.class);
+                    final boolean ethernetAvailable = (em != null);
                     if (!ethernetAvailable) {
                         keys.add(KEY_ENABLE_ETHERNET_TETHERING);
                     }
@@ -646,9 +649,15 @@
         }
     }
 
-    private final class EthernetListener implements EthernetManager.Listener {
-        public void onAvailabilityChanged(String iface, boolean isAvailable) {
-            mHandler.post(() -> updateBluetoothAndEthernetState());
+    private final class EthernetListener implements EthernetManager.InterfaceStateListener {
+        public void onInterfaceStateChanged(@NonNull String iface, @InterfaceState int state,
+                @Role int role, @NonNull IpConfiguration configuration) {
+            if (state == EthernetManager.STATE_LINK_UP) {
+                mAvailableInterfaces.add(iface);
+            } else {
+                mAvailableInterfaces.remove(iface);
+            }
+            updateBluetoothAndEthernetState();
         }
     }
 }
diff --git a/src/com/android/settings/applications/specialaccess/notificationaccess/BridgedAppsPreferenceController.java b/src/com/android/settings/applications/specialaccess/notificationaccess/BridgedAppsPreferenceController.java
index c56f630..8e2d3d4 100644
--- a/src/com/android/settings/applications/specialaccess/notificationaccess/BridgedAppsPreferenceController.java
+++ b/src/com/android/settings/applications/specialaccess/notificationaccess/BridgedAppsPreferenceController.java
@@ -25,6 +25,7 @@
 import com.android.settings.applications.AppStateBaseBridge;
 import com.android.settings.core.BasePreferenceController;
 import com.android.settings.notification.NotificationBackend;
+import com.android.settingslib.applications.AppUtils;
 import com.android.settingslib.applications.ApplicationsState;
 import com.android.settingslib.applications.ApplicationsState.AppEntry;
 import com.android.settingslib.applications.ApplicationsState.AppFilter;
@@ -128,7 +129,7 @@
             CheckBoxPreference preference = mScreen.findPreference(prefKey);
             if (preference == null) {
                 preference = new CheckBoxPreference(mScreen.getContext());
-                preference.setIcon(entry.icon);
+                preference.setIcon(AppUtils.getIcon(mContext, entry));
                 preference.setTitle(entry.label);
                 preference.setKey(prefKey);
                 mScreen.addPreference(preference);
diff --git a/src/com/android/settings/development/tare/AlarmManagerFragment.java b/src/com/android/settings/development/tare/AlarmManagerFragment.java
index dbc4e58..fe76b12 100644
--- a/src/com/android/settings/development/tare/AlarmManagerFragment.java
+++ b/src/com/android/settings/development/tare/AlarmManagerFragment.java
@@ -15,17 +15,16 @@
  */
 package com.android.settings.development.tare;
 
+import android.annotation.Nullable;
 import android.app.Fragment;
+import android.app.tare.EconomyManager;
 import android.content.res.Resources;
 import android.os.Bundle;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.BaseExpandableListAdapter;
 import android.widget.ExpandableListView;
 import android.widget.ExpandableListView.OnChildClickListener;
-import android.widget.TextView;
-import android.widget.Toast;
 
 import com.android.settings.R;
 
@@ -33,130 +32,96 @@
  * Creates the AlarmManager fragment to display all the AlarmManager factors
  * when the AlarmManager policy is chosen in the dropdown TARE menu.
  */
-public class AlarmManagerFragment extends Fragment {
+public class AlarmManagerFragment extends Fragment implements
+        TareFactorController.DataChangeListener {
+
+    private TareFactorController mFactorController;
+
+    private TareFactorExpandableListAdapter mExpandableListAdapter;
+
+    private String[] mGroups;
+    private String[][] mChildren;
+    private String[][] mKeys;
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mFactorController = TareFactorController.getInstance(getContext());
+        populateArrays();
+    }
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
         View v = inflater.inflate(R.layout.tare_policy_fragment, null);
         ExpandableListView elv = (ExpandableListView) v.findViewById(R.id.factor_list);
-        final SavedTabsListAdapter expListAdapter = new SavedTabsListAdapter();
+        mExpandableListAdapter = new TareFactorExpandableListAdapter(
+                mFactorController, LayoutInflater.from(getActivity()), mGroups, mChildren, mKeys);
         elv.setGroupIndicator(null);
-        elv.setAdapter(expListAdapter);
+        elv.setAdapter(mExpandableListAdapter);
         elv.setOnChildClickListener(new OnChildClickListener() {
             public boolean onChildClick(ExpandableListView parent, View v,
                     int groupPosition, int childPosition, long id) {
-                final String selected =
-                        (String) expListAdapter.getChild(groupPosition, childPosition);
-                Toast.makeText(getActivity(), selected, Toast.LENGTH_SHORT).show();
+                final String key = mExpandableListAdapter.getKey(groupPosition, childPosition);
+                mFactorController.createDialog(key).show(getFragmentManager(), key);
                 return true;
             }
         });
         return v;
     }
 
-     /**
-     * Creates the expandable list containing all AlarmManager factors within the
-     * AlarmManager fragment.
-     */
-    public class SavedTabsListAdapter extends BaseExpandableListAdapter {
+    @Override
+    public void onStart() {
+        super.onStart();
+        mFactorController.registerListener(this);
+    }
 
-        private final LayoutInflater mInflater;
-        private Resources mResources = getActivity().getResources();
+    @Override
+    public void onStop() {
+        mFactorController.unregisterListener(this);
+        super.onStop();
+    }
 
-        private String[] mGroups = {
-                mResources.getString(R.string.tare_max_circulation),
-                mResources.getString(R.string.tare_max_satiated_balance),
-                mResources.getString(R.string.tare_min_satiated_balance),
-                mResources.getString(R.string.tare_modifiers),
-                mResources.getString(R.string.tare_actions),
-                mResources.getString(R.string.tare_rewards)
+    @Override
+    public void onDataChanged() {
+        mExpandableListAdapter.notifyDataSetChanged();
+    }
+
+    private void populateArrays() {
+        final Resources resources = getResources();
+
+        mGroups = new String[]{
+                resources.getString(R.string.tare_consumption_limits),
+                resources.getString(R.string.tare_balances),
+                // resources.getString(R.string.tare_modifiers),
+                // resources.getString(R.string.tare_actions),
+                // resources.getString(R.string.tare_rewards)
         };
 
-        /*
-         * First two are empty arrays because the first two factors have no subfactors (no
-         * children).
-         */
-        private String[][] mChildren = {
-                {},
-                {},
-                mResources.getStringArray(R.array.tare_min_satiated_balance_subfactors),
-                mResources.getStringArray(R.array.tare_modifiers_subfactors),
-                mResources.getStringArray(R.array.tare_alarm_manager_actions),
-                mResources.getStringArray(R.array.tare_rewards_subfactors)
+        mChildren = new String[][]{
+                resources.getStringArray(R.array.tare_consumption_limit_subfactors),
+                resources.getStringArray(R.array.tare_app_balance_subfactors),
+                // TODO: support
+                // resources.getStringArray(R.array.tare_modifiers_subfactors),
+                // resources.getStringArray(R.array.tare_alarm_manager_actions),
+                // resources.getStringArray(R.array.tare_rewards_subfactors)
         };
 
-        public SavedTabsListAdapter() {
-            mInflater = LayoutInflater.from(getActivity());
-        }
-
-        @Override
-        public int getGroupCount() {
-            return mGroups.length;
-        }
-
-        @Override
-        public int getChildrenCount(int groupPosition) {
-            return mChildren[groupPosition].length;
-        }
-
-        @Override
-        public Object getGroup(int groupPosition) {
-            return mGroups[groupPosition];
-        }
-
-        @Override
-        public Object getChild(int groupPosition, int childPosition) {
-            return mChildren[groupPosition][childPosition];
-        }
-
-        @Override
-        public long getGroupId(int groupPosition) {
-            return groupPosition;
-        }
-
-        @Override
-        public long getChildId(int groupPosition, int childPosition) {
-            return childPosition;
-        }
-
-        @Override
-        public boolean hasStableIds() {
-            return true;
-        }
-
-        @Override
-        public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
-                ViewGroup parent) {
-            if (convertView == null) {
-                convertView = mInflater.inflate(android.R.layout.simple_list_item_1, parent, false);
-            }
-            TextView factor = (TextView) convertView.findViewById(android.R.id.text1);
-            factor.setText(getGroup(groupPosition).toString());
-            return convertView;
-        }
-
-        @Override
-        public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
-                View convertView, ViewGroup parent) {
-            // Here a custom child item is used instead of android.R.simple_list_item_2 because it
-            // is more customizable for this specific UI
-            if (convertView == null) {
-                convertView = mInflater.inflate(R.layout.tare_child_item, null);
-            }
-            TextView factor = (TextView) convertView.findViewById(R.id.factor);
-            TextView value = (TextView) convertView.findViewById(R.id.factor_number);
-
-            // TODO: Replace these hardcoded values with either default or user inputted TARE values
-            factor.setText(getChild(groupPosition, childPosition).toString());
-            value.setText("500");
-
-            return convertView;
-        }
-
-        @Override
-        public boolean isChildSelectable(int groupPosition, int childPosition) {
-            return true;
-        }
+        mKeys = new String[][]{
+                {
+                        EconomyManager.KEY_AM_INITIAL_CONSUMPTION_LIMIT,
+                        EconomyManager.KEY_AM_HARD_CONSUMPTION_LIMIT
+                },
+                {
+                        EconomyManager.KEY_AM_MAX_SATIATED_BALANCE,
+                        EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED,
+                        EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP,
+                        EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP
+                },
+                // {},
+                // {},
+                // {},
+        };
     }
 }
diff --git a/src/com/android/settings/development/tare/DropdownActivity.java b/src/com/android/settings/development/tare/DropdownActivity.java
index 55f1fec..66b57dd 100644
--- a/src/com/android/settings/development/tare/DropdownActivity.java
+++ b/src/com/android/settings/development/tare/DropdownActivity.java
@@ -43,7 +43,6 @@
     static final int POLICY_JOB_SCHEDULER = 1;
     private static final int DEFAULT_POLICY = POLICY_ALARM_MANAGER;
 
-
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
diff --git a/src/com/android/settings/development/tare/JobSchedulerFragment.java b/src/com/android/settings/development/tare/JobSchedulerFragment.java
index 5a7f4a9..1c6598c 100644
--- a/src/com/android/settings/development/tare/JobSchedulerFragment.java
+++ b/src/com/android/settings/development/tare/JobSchedulerFragment.java
@@ -15,17 +15,16 @@
  */
 package com.android.settings.development.tare;
 
+import android.annotation.Nullable;
 import android.app.Fragment;
+import android.app.tare.EconomyManager;
 import android.content.res.Resources;
 import android.os.Bundle;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.BaseExpandableListAdapter;
 import android.widget.ExpandableListView;
 import android.widget.ExpandableListView.OnChildClickListener;
-import android.widget.TextView;
-import android.widget.Toast;
 
 import com.android.settings.R;
 
@@ -33,131 +32,97 @@
  * Creates the JobScheduler fragment to display all the JobScheduler factors
  * when the JobScheduler policy is chosen in the dropdown TARE menu.
  */
-public class JobSchedulerFragment extends Fragment {
+public class JobSchedulerFragment extends Fragment implements
+        TareFactorController.DataChangeListener {
+
+    private TareFactorController mFactorController;
+
+    private TareFactorExpandableListAdapter mExpandableListAdapter;
+
+    private String[] mGroups;
+    private String[][] mChildren;
+    private String[][] mKeys;
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mFactorController = TareFactorController.getInstance(getContext());
+        populateArrays();
+    }
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
         View v = inflater.inflate(R.layout.tare_policy_fragment, null);
         ExpandableListView elv = (ExpandableListView) v.findViewById(R.id.factor_list);
-        final SavedTabsListAdapter expListAdapter = new SavedTabsListAdapter();
+        mExpandableListAdapter = new TareFactorExpandableListAdapter(
+                mFactorController, LayoutInflater.from(getActivity()), mGroups, mChildren, mKeys);
         elv.setGroupIndicator(null);
-        elv.setAdapter(expListAdapter);
+        elv.setAdapter(mExpandableListAdapter);
         elv.setOnChildClickListener(new OnChildClickListener() {
             public boolean onChildClick(ExpandableListView parent, View v,
                     int groupPosition, int childPosition, long id) {
-                final String selected =
-                        (String) expListAdapter.getChild(groupPosition, childPosition);
-                Toast.makeText(getActivity(), selected, Toast.LENGTH_SHORT)
-                        .show();
+                final String key = mExpandableListAdapter.getKey(groupPosition, childPosition);
+                mFactorController.createDialog(key).show(getFragmentManager(), key);
                 return true;
             }
         });
+
         return v;
     }
 
-    /**
-     * Creates the expandable list containing all JobScheduler factors within the
-     * JobScheduler fragment.
-     */
-    public class SavedTabsListAdapter extends BaseExpandableListAdapter {
+    @Override
+    public void onStart() {
+        super.onStart();
+        mFactorController.registerListener(this);
+    }
 
-        private final LayoutInflater mInflater;
-        private Resources mResources = getActivity().getResources();
+    @Override
+    public void onStop() {
+        mFactorController.unregisterListener(this);
+        super.onStop();
+    }
 
-        private String[] mGroups = {
-                mResources.getString(R.string.tare_max_circulation),
-                mResources.getString(R.string.tare_max_satiated_balance),
-                mResources.getString(R.string.tare_min_satiated_balance),
-                mResources.getString(R.string.tare_modifiers),
-                mResources.getString(R.string.tare_actions),
-                mResources.getString(R.string.tare_rewards)
+    @Override
+    public void onDataChanged() {
+        mExpandableListAdapter.notifyDataSetChanged();
+    }
+
+    private void populateArrays() {
+        final Resources resources = getResources();
+
+        mGroups = new String[]{
+                resources.getString(R.string.tare_consumption_limits),
+                resources.getString(R.string.tare_balances),
+                // mResources.getString(R.string.tare_modifiers),
+                // mResources.getString(R.string.tare_actions),
+                // mResources.getString(R.string.tare_rewards)
         };
 
-        /*
-         * First two are empty arrays because the first two factors have no subfactors (no
-         * children).
-         */
-        private String[][] mChildren = {
-                {},
-                {},
-                mResources.getStringArray(R.array.tare_min_satiated_balance_subfactors),
-                mResources.getStringArray(R.array.tare_modifiers_subfactors),
-                mResources.getStringArray(R.array.tare_job_scheduler_actions),
-                mResources.getStringArray(R.array.tare_rewards_subfactors)
+        mChildren = new String[][]{
+                resources.getStringArray(R.array.tare_consumption_limit_subfactors),
+                resources.getStringArray(R.array.tare_app_balance_subfactors),
+                // TODO: support
+                // mResources.getStringArray(R.array.tare_modifiers_subfactors),
+                // mResources.getStringArray(R.array.tare_job_scheduler_actions),
+                // mResources.getStringArray(R.array.tare_rewards_subfactors)
         };
 
-        public SavedTabsListAdapter() {
-            mInflater = LayoutInflater.from(getActivity());
-        }
-
-        @Override
-        public int getGroupCount() {
-            return mGroups.length;
-        }
-
-        @Override
-        public int getChildrenCount(int groupPosition) {
-            return mChildren[groupPosition].length;
-        }
-
-        @Override
-        public Object getGroup(int groupPosition) {
-            return mGroups[groupPosition];
-        }
-
-        @Override
-        public Object getChild(int groupPosition, int childPosition) {
-            return mChildren[groupPosition][childPosition];
-        }
-
-        @Override
-        public long getGroupId(int groupPosition) {
-            return groupPosition;
-        }
-
-        @Override
-        public long getChildId(int groupPosition, int childPosition) {
-            return childPosition;
-        }
-
-        @Override
-        public boolean hasStableIds() {
-            return true;
-        }
-
-        @Override
-        public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
-                ViewGroup parent) {
-            if (convertView == null) {
-                convertView = mInflater.inflate(android.R.layout.simple_list_item_1, parent, false);
-            }
-            TextView factor = (TextView) convertView.findViewById(android.R.id.text1);
-            factor.setText(getGroup(groupPosition).toString());
-            return convertView;
-        }
-
-        @Override
-        public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
-                View convertView, ViewGroup parent) {
-            // Here a custom child item is used instead of android.R.simple_list_item_2 because it
-            // is more customizable for this specific UI
-            if (convertView == null) {
-                convertView = mInflater.inflate(R.layout.tare_child_item, null);
-            }
-            TextView factor = (TextView) convertView.findViewById(R.id.factor);
-            TextView value = (TextView) convertView.findViewById(R.id.factor_number);
-
-            // TODO: Replace these hardcoded values with either default or user inputted TARE values
-            factor.setText(getChild(groupPosition, childPosition).toString());
-            value.setText("500");
-
-            return convertView;
-        }
-
-        @Override
-        public boolean isChildSelectable(int groupPosition, int childPosition) {
-            return true;
-        }
+        mKeys = new String[][]{
+                {
+                        EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT,
+                        EconomyManager.KEY_JS_HARD_CONSUMPTION_LIMIT
+                },
+                {
+                        EconomyManager.KEY_JS_MAX_SATIATED_BALANCE,
+                        EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED,
+                        EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP,
+                        EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP
+                },
+                // {},
+                // {},
+                // {},
+        };
     }
 }
diff --git a/src/com/android/settings/development/tare/TareFactorController.java b/src/com/android/settings/development/tare/TareFactorController.java
index 7e9f314..b9f813d 100644
--- a/src/com/android/settings/development/tare/TareFactorController.java
+++ b/src/com/android/settings/development/tare/TareFactorController.java
@@ -16,12 +16,21 @@
 
 package com.android.settings.development.tare;
 
+import static android.provider.Settings.Global.TARE_ALARM_MANAGER_CONSTANTS;
+import static android.provider.Settings.Global.TARE_JOB_SCHEDULER_CONSTANTS;
+
+import android.annotation.NonNull;
 import android.app.tare.EconomyManager;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
 import android.provider.Settings;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.KeyValueListParser;
 import android.util.Slog;
 
@@ -34,6 +43,8 @@
 public class TareFactorController {
     private static final String TAG = "TareFactorController";
 
+    private static TareFactorController sInstance;
+
     private static final int POLICY_ALARM_MANAGER = 0;
     private static final int POLICY_JOB_SCHEDULER = 1;
 
@@ -45,15 +56,19 @@
     private String mAlarmManagerConstants;
     private String mJobSchedulerConstants;
 
-    public TareFactorController(Context context) {
+    private final ArraySet<DataChangeListener> mDataChangeListeners = new ArraySet<>();
+
+    private TareFactorController(Context context) {
         mContentResolver = context.getContentResolver();
         mResources = context.getResources();
 
-        mAlarmManagerConstants = Settings.Global
-                .getString(mContentResolver, Settings.Global.TARE_ALARM_MANAGER_CONSTANTS);
+        ConfigObserver configObserver = new ConfigObserver(new Handler(Looper.getMainLooper()));
+        configObserver.start();
 
-        mJobSchedulerConstants = Settings.Global
-                .getString(mContentResolver, Settings.Global.TARE_JOB_SCHEDULER_CONSTANTS);
+        mAlarmManagerConstants =
+                Settings.Global.getString(mContentResolver, TARE_ALARM_MANAGER_CONSTANTS);
+        mJobSchedulerConstants =
+                Settings.Global.getString(mContentResolver, TARE_JOB_SCHEDULER_CONSTANTS);
 
         initAlarmManagerMap();
         parseAlarmManagerGlobalSettings();
@@ -62,21 +77,30 @@
         parseJobSchedulerGlobalSettings();
     }
 
+    static TareFactorController getInstance(Context context) {
+        synchronized (TareFactorController.class) {
+            if (sInstance == null) {
+                sInstance = new TareFactorController(context.getApplicationContext());
+            }
+        }
+        return sInstance;
+    }
+
     /**
      * Initialization for AlarmManager Map that sets a AM factor key to a title, default value, and
      * policy type in a data object.
      */
     private void initAlarmManagerMap() {
         mAlarmManagerMap.put(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED,
-                new TareFactorData(mResources.getString(R.string.tare_min_satiated_balance),
+                new TareFactorData(mResources.getString(R.string.tare_min_balance_exempted),
                         EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED,
                         POLICY_ALARM_MANAGER));
         mAlarmManagerMap.put(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP,
-                new TareFactorData(mResources.getString(R.string.tare_headless_app),
+                new TareFactorData(mResources.getString(R.string.tare_min_balance_headless_app),
                         EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP,
                         POLICY_ALARM_MANAGER));
         mAlarmManagerMap.put(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP,
-                new TareFactorData(mResources.getString(R.string.tare_other_app),
+                new TareFactorData(mResources.getString(R.string.tare_min_balance_other_app),
                         EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP,
                         POLICY_ALARM_MANAGER));
         mAlarmManagerMap.put(EconomyManager.KEY_AM_MAX_SATIATED_BALANCE,
@@ -84,9 +108,13 @@
                         EconomyManager.DEFAULT_AM_MAX_SATIATED_BALANCE,
                         POLICY_ALARM_MANAGER));
         mAlarmManagerMap.put(EconomyManager.KEY_AM_INITIAL_CONSUMPTION_LIMIT,
-                new TareFactorData(mResources.getString(R.string.tare_max_circulation),
+                new TareFactorData(mResources.getString(R.string.tare_initial_consumption_limit),
                         EconomyManager.DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT,
                         POLICY_ALARM_MANAGER));
+        mAlarmManagerMap.put(EconomyManager.KEY_AM_HARD_CONSUMPTION_LIMIT,
+                new TareFactorData(mResources.getString(R.string.tare_hard_consumption_limit),
+                        EconomyManager.DEFAULT_AM_HARD_CONSUMPTION_LIMIT,
+                        POLICY_ALARM_MANAGER));
         mAlarmManagerMap.put(EconomyManager.KEY_AM_REWARD_TOP_ACTIVITY_INSTANT,
                 new TareFactorData(mResources.getString(R.string.tare_top_activity),
                         EconomyManager.DEFAULT_AM_REWARD_TOP_ACTIVITY_INSTANT,
@@ -250,15 +278,15 @@
      */
     private void initJobSchedulerMap() {
         mJobSchedulerMap.put(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED,
-                new TareFactorData(mResources.getString(R.string.tare_min_satiated_balance),
+                new TareFactorData(mResources.getString(R.string.tare_min_balance_exempted),
                         EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED,
                         POLICY_JOB_SCHEDULER));
         mJobSchedulerMap.put(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP,
-                new TareFactorData(mResources.getString(R.string.tare_headless_app),
+                new TareFactorData(mResources.getString(R.string.tare_min_balance_headless_app),
                         EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP,
                         POLICY_JOB_SCHEDULER));
         mJobSchedulerMap.put(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP,
-                new TareFactorData(mResources.getString(R.string.tare_other_app),
+                new TareFactorData(mResources.getString(R.string.tare_min_balance_other_app),
                         EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP,
                         POLICY_JOB_SCHEDULER));
         mJobSchedulerMap.put(EconomyManager.KEY_JS_MAX_SATIATED_BALANCE,
@@ -266,9 +294,13 @@
                         EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE,
                         POLICY_JOB_SCHEDULER));
         mJobSchedulerMap.put(EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT,
-                new TareFactorData(mResources.getString(R.string.tare_max_circulation),
+                new TareFactorData(mResources.getString(R.string.tare_initial_consumption_limit),
                         EconomyManager.DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT,
                         POLICY_JOB_SCHEDULER));
+        mJobSchedulerMap.put(EconomyManager.KEY_JS_HARD_CONSUMPTION_LIMIT,
+                new TareFactorData(mResources.getString(R.string.tare_hard_consumption_limit),
+                        EconomyManager.DEFAULT_JS_HARD_CONSUMPTION_LIMIT,
+                        POLICY_JOB_SCHEDULER));
         mJobSchedulerMap.put(EconomyManager.KEY_JS_REWARD_TOP_ACTIVITY_INSTANT,
                 new TareFactorData(mResources.getString(R.string.tare_top_activity),
                         EconomyManager.DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT,
@@ -408,8 +440,7 @@
         mJobSchedulerMap.put(
                 EconomyManager.KEY_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE,
                 new TareFactorData(mResources.getString(R.string.tare_job_low_running),
-                        EconomyManager
-                                .DEFAULT_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE,
+                        EconomyManager.DEFAULT_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE,
                         POLICY_JOB_SCHEDULER));
         mJobSchedulerMap.put(EconomyManager.KEY_JS_ACTION_JOB_MIN_START_BASE_PRICE,
                 new TareFactorData(mResources.getString(R.string.tare_job_min_start),
@@ -425,103 +456,65 @@
                         POLICY_JOB_SCHEDULER));
     }
 
-
-    /**
-     * Takes a key and factor policy as input and grabs the default value linked to it.
-     *
-     * @param key the key of the factor you want to get the default value of
-     * @param factorPolicy the policy you want the default value of
-     */
-    private int getDefaultValue(String key, int factorPolicy) {
-        ArrayMap<String, TareFactorData> currentMap;
-        switch (factorPolicy) {
-            case POLICY_ALARM_MANAGER:
-                currentMap = mAlarmManagerMap;
-                break;
-            case POLICY_JOB_SCHEDULER:
-                currentMap = mJobSchedulerMap;
-                break;
-            default:
-                throw new IllegalArgumentException("Invalid factor policy given");
-        }
-        return currentMap.get(key).defaultValue;
-    }
-
     /**
      * Parses the AM constant from Settings.Global to get to the current value.
      */
     private void parseAlarmManagerGlobalSettings() {
-        try {
-            mParser.setString(mAlarmManagerConstants);
-        } catch (Exception e) {
-            Slog.e(TAG, "Bad value string constants", e);
-        }
-        int size = mParser.size();
-
-        for (int i = 0; i < size - 1; i++) {
-            String key = mParser.keyAt(i);
-            TareFactorData data = mAlarmManagerMap.get(key);
-            data.currentValue = mParser.getInt(key, getDefaultValue(key, getFactorType(key)));
-        }
+        parseSettingsIntoMap(mAlarmManagerConstants, mAlarmManagerMap);
     }
 
     /**
      * Parses the JS constant from Settings.Global to get to the current value.
      */
     private void parseJobSchedulerGlobalSettings() {
-        try {
-            mParser.setString(mJobSchedulerConstants);
-        } catch (Exception e) {
-            Slog.e(TAG, "Bad value string constants", e);
-        }
-        int size = mParser.size();
+        parseSettingsIntoMap(mJobSchedulerConstants, mJobSchedulerMap);
+    }
 
-        for (int i = 0; i < size - 1; i++) {
-            String key = mParser.keyAt(i);
-            TareFactorData data = mJobSchedulerMap.get(key);
-            data.currentValue = mParser.getInt(key, getDefaultValue(key, getFactorType(key)));
+    private void parseSettingsIntoMap(String constants, ArrayMap<String, TareFactorData> map) {
+        try {
+            mParser.setString(constants);
+        } catch (Exception e) {
+            Slog.e(TAG, "Bad string constants value", e);
+        }
+
+        for (int i = map.size() - 1; i >= 0; --i) {
+            final String key = map.keyAt(i);
+            final TareFactorData data = map.valueAt(i);
+            data.currentValue = mParser.getInt(key, data.defaultValue);
+        }
+    }
+
+    @NonNull
+    private ArrayMap<String, TareFactorData> getMap(int factorPolicy) {
+        switch (factorPolicy) {
+            case POLICY_ALARM_MANAGER:
+                return mAlarmManagerMap;
+            case POLICY_JOB_SCHEDULER:
+                return mJobSchedulerMap;
+            default:
+                throw new IllegalArgumentException("Invalid factor policy given");
         }
     }
 
     /**
      * Takes a key and factor policy as input and grabs the title linked to it.
      *
-     * @param key the key of the factor you want to get the title of
+     * @param key          the key of the factor you want to get the title of
      * @param factorPolicy the policy you want the title of
      */
     private String getTitle(String key, int factorPolicy) {
-        ArrayMap<String, TareFactorData> currentMap;
-        switch (factorPolicy) {
-            case POLICY_ALARM_MANAGER:
-                currentMap = mAlarmManagerMap;
-                break;
-            case POLICY_JOB_SCHEDULER:
-                currentMap = mJobSchedulerMap;
-                break;
-            default:
-                throw new IllegalArgumentException("Invalid factor policy given");
-        }
+        final ArrayMap<String, TareFactorData> currentMap = getMap(factorPolicy);
         return currentMap.get(key).title;
     }
 
     /**
      * Takes a key and factor policy as input and grabs the current value linked to it.
      *
-     * @param key the key of the factor you want to get the default value of
+     * @param key          the key of the factor you want to get the default value of
      * @param factorPolicy the policy you want the current value of
      */
     private int getCurrentValue(String key, int factorPolicy) {
-        ArrayMap<String, TareFactorData> currentMap;
-        switch (factorPolicy) {
-            case POLICY_ALARM_MANAGER:
-                currentMap = mAlarmManagerMap;
-                break;
-            case POLICY_JOB_SCHEDULER:
-                currentMap = mJobSchedulerMap;
-                break;
-            default:
-                throw new IllegalArgumentException("Invalid factor policy given");
-        }
+        final ArrayMap<String, TareFactorData> currentMap = getMap(factorPolicy);
         return currentMap.get(key).currentValue;
     }
 
@@ -542,6 +535,11 @@
         return currentMap.get(key).factorPolicy;
     }
 
+    int getValue(String key) {
+        final int policy = getFactorType(key);
+        return getCurrentValue(key, policy);
+    }
+
     /**
      * Takes a key,edited value, and factor policy as input and assigns the new edited value to
      * be the new current value for that factors key.
@@ -551,18 +549,14 @@
      * @param factorPolicy policy being updated
      */
     public void updateValue(String key, int editedValue, int factorPolicy) {
-        switch (factorPolicy) {
-            case POLICY_ALARM_MANAGER:
-                mAlarmManagerMap.get(key).currentValue = editedValue;
-                rebuildPolicyConstants(factorPolicy);
-                break;
-            case POLICY_JOB_SCHEDULER:
-                mJobSchedulerMap.get(key).currentValue = editedValue;
-                rebuildPolicyConstants(factorPolicy);
-                break;
-            default:
-                throw new IllegalArgumentException("Invalid factor policy given");
+        final ArrayMap<String, TareFactorData> map = getMap(factorPolicy);
+
+        final TareFactorData data = map.get(key);
+        if (data.currentValue == editedValue) {
+            return;
         }
+        data.currentValue = editedValue;
+        rebuildPolicyConstants(factorPolicy);
     }
 
     /**
@@ -574,20 +568,10 @@
     private void rebuildPolicyConstants(int factorPolicy) {
         switch (factorPolicy) {
             case POLICY_ALARM_MANAGER:
-                writeConstantsToSettings(mAlarmManagerMap,
-                        Settings.Global.TARE_ALARM_MANAGER_CONSTANTS);
-
-                mAlarmManagerConstants = Settings.Global
-                        .getString(mContentResolver, Settings.Global
-                                .TARE_ALARM_MANAGER_CONSTANTS);
+                writeConstantsToSettings(mAlarmManagerMap, TARE_ALARM_MANAGER_CONSTANTS);
                 break;
             case POLICY_JOB_SCHEDULER:
-                writeConstantsToSettings(mJobSchedulerMap,
-                        Settings.Global.TARE_JOB_SCHEDULER_CONSTANTS);
-
-                mJobSchedulerConstants = Settings.Global
-                        .getString(mContentResolver, Settings.Global
-                                .TARE_JOB_SCHEDULER_CONSTANTS);
+                writeConstantsToSettings(mJobSchedulerMap, TARE_JOB_SCHEDULER_CONSTANTS);
                 break;
         }
     }
@@ -623,7 +607,7 @@
     public TareFactorDialogFragment createDialog(String key) {
         int policy = getFactorType(key);
         return new TareFactorDialogFragment(getTitle(key, policy), key,
-                getCurrentValue(key, policy), policy , this);
+                getCurrentValue(key, policy), policy, this);
     }
 
     /**
@@ -642,4 +626,51 @@
             this.currentValue = defaultValue;
         }
     }
-}
\ No newline at end of file
+
+    interface DataChangeListener {
+        void onDataChanged();
+    }
+
+    void registerListener(DataChangeListener listener) {
+        mDataChangeListeners.add(listener);
+    }
+
+    void unregisterListener(DataChangeListener listener) {
+        mDataChangeListeners.remove(listener);
+    }
+
+    void notifyListeners() {
+        for (int i = mDataChangeListeners.size() - 1; i >= 0; --i) {
+            mDataChangeListeners.valueAt(i).onDataChanged();
+        }
+    }
+
+    private class ConfigObserver extends ContentObserver {
+
+        ConfigObserver(Handler handler) {
+            super(handler);
+        }
+
+        public void start() {
+            mContentResolver.registerContentObserver(
+                    Settings.Global.getUriFor(TARE_ALARM_MANAGER_CONSTANTS), false, this);
+            mContentResolver.registerContentObserver(
+                    Settings.Global.getUriFor(TARE_JOB_SCHEDULER_CONSTANTS), false, this);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            if (uri.equals(Settings.Global.getUriFor(TARE_ALARM_MANAGER_CONSTANTS))) {
+                mAlarmManagerConstants =
+                        Settings.Global.getString(mContentResolver, TARE_ALARM_MANAGER_CONSTANTS);
+                parseAlarmManagerGlobalSettings();
+                notifyListeners();
+            } else if (uri.equals(Settings.Global.getUriFor(TARE_JOB_SCHEDULER_CONSTANTS))) {
+                mJobSchedulerConstants =
+                        Settings.Global.getString(mContentResolver, TARE_JOB_SCHEDULER_CONSTANTS);
+                parseJobSchedulerGlobalSettings();
+                notifyListeners();
+            }
+        }
+    }
+}
diff --git a/src/com/android/settings/development/tare/TareFactorDialogFragment.java b/src/com/android/settings/development/tare/TareFactorDialogFragment.java
index ff7f5f9..8d2f341 100644
--- a/src/com/android/settings/development/tare/TareFactorDialogFragment.java
+++ b/src/com/android/settings/development/tare/TareFactorDialogFragment.java
@@ -16,7 +16,10 @@
 
 package com.android.settings.development.tare;
 
+import android.annotation.NonNull;
+import android.app.AlertDialog;
 import android.app.Dialog;
+import android.app.DialogFragment;
 import android.content.Context;
 import android.os.Bundle;
 import android.text.InputType;
@@ -25,10 +28,6 @@
 import android.view.View;
 import android.widget.EditText;
 
-import androidx.annotation.NonNull;
-import androidx.appcompat.app.AlertDialog;
-import androidx.fragment.app.DialogFragment;
-
 import com.android.settings.R;
 import com.android.settings.Utils;
 
diff --git a/src/com/android/settings/development/tare/TareFactorExpandableListAdapter.java b/src/com/android/settings/development/tare/TareFactorExpandableListAdapter.java
new file mode 100644
index 0000000..8fe4c05
--- /dev/null
+++ b/src/com/android/settings/development/tare/TareFactorExpandableListAdapter.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2022 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.settings.development.tare;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseExpandableListAdapter;
+import android.widget.TextView;
+
+import com.android.settings.R;
+
+/**
+ * Creates the expandable list that will allow modifying individual factors.
+ */
+public class TareFactorExpandableListAdapter extends BaseExpandableListAdapter {
+
+    private final LayoutInflater mLayoutInflater;
+    private final TareFactorController mFactorController;
+
+    private final String[] mGroups;
+    private final String[][] mChildren;
+    private final String[][] mKeys;
+
+    TareFactorExpandableListAdapter(TareFactorController factorController,
+            LayoutInflater layoutInflater, String[] groups, String[][] children, String[][] keys) {
+        mLayoutInflater = layoutInflater;
+        mFactorController = factorController;
+
+        mGroups = groups;
+        mChildren = children;
+        mKeys = keys;
+
+        validateMappings();
+    }
+
+    private void validateMappings() {
+        if (mGroups.length != mChildren.length) {
+            throw new IllegalStateException("groups and children don't have the same length");
+        }
+        if (mChildren.length != mKeys.length) {
+            throw new IllegalStateException("children and keys don't have the same length");
+        }
+        for (int i = 0; i < mChildren.length; ++i) {
+            if (mChildren[i].length != mKeys[i].length) {
+                throw new IllegalStateException(
+                        "children and keys don't have the same length in row " + i);
+            }
+        }
+    }
+
+    @Override
+    public int getGroupCount() {
+        return mGroups.length;
+    }
+
+    @Override
+    public int getChildrenCount(int groupPosition) {
+        return mChildren[groupPosition].length;
+    }
+
+    @Override
+    public Object getGroup(int groupPosition) {
+        return mGroups[groupPosition];
+    }
+
+    @Override
+    public Object getChild(int groupPosition, int childPosition) {
+        return mChildren[groupPosition][childPosition];
+    }
+
+    @Override
+    public long getGroupId(int groupPosition) {
+        return groupPosition;
+    }
+
+    @Override
+    public long getChildId(int groupPosition, int childPosition) {
+        return childPosition;
+    }
+
+    @NonNull
+    String getKey(int groupPosition, int childPosition) {
+        return mKeys[groupPosition][childPosition];
+    }
+
+    @Override
+    public boolean hasStableIds() {
+        return true;
+    }
+
+    @Override
+    public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
+            ViewGroup parent) {
+        if (convertView == null) {
+            convertView = mLayoutInflater.inflate(android.R.layout.simple_list_item_1, parent,
+                    false);
+        }
+        TextView factor = convertView.findViewById(android.R.id.text1);
+        factor.setText(getGroup(groupPosition).toString());
+        return convertView;
+    }
+
+    @Override
+    @SuppressLint("InflateParams") // AdapterView doesn't support addView
+    public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
+            View convertView, ViewGroup parent) {
+        // Here a custom child item is used instead of android.R.simple_list_item_2 because it
+        // is more customizable for this specific UI
+        if (convertView == null) {
+            convertView = mLayoutInflater.inflate(R.layout.tare_child_item, null);
+        }
+        TextView factor = convertView.findViewById(R.id.factor);
+        TextView value = convertView.findViewById(R.id.factor_number);
+
+        factor.setText(getChild(groupPosition, childPosition).toString());
+        value.setText(String.valueOf(
+                mFactorController.getValue(getKey(groupPosition, childPosition))));
+
+        return convertView;
+    }
+
+    @Override
+    public boolean isChildSelectable(int groupPosition, int childPosition) {
+        return true;
+    }
+}
diff --git a/src/com/android/settings/development/tare/TareHomePage.java b/src/com/android/settings/development/tare/TareHomePage.java
index 38e7ed8..cea0954 100644
--- a/src/com/android/settings/development/tare/TareHomePage.java
+++ b/src/com/android/settings/development/tare/TareHomePage.java
@@ -69,11 +69,14 @@
     }
 
     /** Reverts the TARE settings to the original default settings */
-    // TODO: Establish default TARE values and make this method revert all settings back to default.
     public void revertSettings(View v) {
         Toast.makeText(this, R.string.tare_settings_reverted_toast, Toast.LENGTH_LONG).show();
         Settings.Global.putString(getApplicationContext().getContentResolver(),
                 Settings.Global.ENABLE_TARE, null);
+        Settings.Global.putString(getApplicationContext().getContentResolver(),
+                Settings.Global.TARE_ALARM_MANAGER_CONSTANTS, null);
+        Settings.Global.putString(getApplicationContext().getContentResolver(),
+                Settings.Global.TARE_JOB_SCHEDULER_CONSTANTS, null);
         setEnabled(Settings.Global.DEFAULT_ENABLE_TARE == SETTING_VALUE_ON);
     }
 
diff --git a/src/com/android/settings/network/EthernetTetherPreferenceController.java b/src/com/android/settings/network/EthernetTetherPreferenceController.java
index 5b2cab7..58c1fd2 100644
--- a/src/com/android/settings/network/EthernetTetherPreferenceController.java
+++ b/src/com/android/settings/network/EthernetTetherPreferenceController.java
@@ -21,52 +21,63 @@
 import android.net.TetheringManager;
 import android.os.Handler;
 import android.os.Looper;
-import android.text.TextUtils;
 
 import androidx.lifecycle.Lifecycle;
 import androidx.lifecycle.OnLifecycleEvent;
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.util.HashSet;
+
 /**
  * This controller helps to manage the switch state and visibility of ethernet tether switch
  * preference.
  */
 public final class EthernetTetherPreferenceController extends TetherBasePreferenceController {
 
-    private final String mEthernetRegex;
+    private final HashSet<String> mAvailableInterfaces = new HashSet<>();
     private final EthernetManager mEthernetManager;
+
     @VisibleForTesting
-    EthernetManager.Listener mEthernetListener;
+    EthernetManager.InterfaceStateListener mEthernetListener;
 
     public EthernetTetherPreferenceController(Context context, String preferenceKey) {
         super(context, preferenceKey);
-        mEthernetRegex = context.getString(
-                com.android.internal.R.string.config_ethernet_iface_regex);
-        mEthernetManager = (EthernetManager) context.getSystemService(Context.ETHERNET_SERVICE);
+        mEthernetManager = context.getSystemService(EthernetManager.class);
     }
 
     @OnLifecycleEvent(Lifecycle.Event.ON_START)
     public void onStart() {
-        mEthernetListener = (iface, isAvailable) -> updateState(mPreference);
+        mEthernetListener = (iface, state, role, configuration) -> {
+            if (state == EthernetManager.STATE_LINK_UP) {
+                mAvailableInterfaces.add(iface);
+            } else {
+                mAvailableInterfaces.remove(iface);
+            }
+            updateState(mPreference);
+        };
         final Handler handler = new Handler(Looper.getMainLooper());
         // Executor will execute to post the updateState event to a new handler which is created
         // from the main looper when the {@link EthernetManager.Listener.onAvailabilityChanged}
         // is triggerd.
-        mEthernetManager.addListener(mEthernetListener, r -> handler.post(r));
+        if (mEthernetManager != null) {
+            mEthernetManager.addInterfaceStateListener(r -> handler.post(r), mEthernetListener);
+        }
     }
 
     @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
     public void onStop() {
-        mEthernetManager.removeListener(mEthernetListener);
-        mEthernetListener = null;
+        if (mEthernetManager != null) {
+            mEthernetManager.removeInterfaceStateListener(mEthernetListener);
+        }
     }
 
     @Override
     public boolean shouldEnable() {
+        ensureRunningOnMainLoopThread();
         String[] available = mTm.getTetherableIfaces();
         for (String s : available) {
-            if (s.matches(mEthernetRegex)) {
+            if (mAvailableInterfaces.contains(s)) {
                 return true;
             }
         }
@@ -75,11 +86,19 @@
 
     @Override
     public boolean shouldShow() {
-        return !TextUtils.isEmpty(mEthernetRegex);
+        return mEthernetManager != null;
     }
 
     @Override
     public int getTetherType() {
         return TetheringManager.TETHERING_ETHERNET;
     }
+
+    private void ensureRunningOnMainLoopThread() {
+        if (Looper.getMainLooper().getThread() != Thread.currentThread()) {
+            throw new IllegalStateException(
+                    "Not running on main loop thread: "
+                            + Thread.currentThread().getName());
+        }
+    }
 }
diff --git a/src/com/android/settings/safetycenter/LockScreenSafetySource.java b/src/com/android/settings/safetycenter/LockScreenSafetySource.java
index 5ebe12a..f059920 100644
--- a/src/com/android/settings/safetycenter/LockScreenSafetySource.java
+++ b/src/com/android/settings/safetycenter/LockScreenSafetySource.java
@@ -57,7 +57,7 @@
                 screenLockPreferenceDetailsUtils);
 
         final SafetySourceStatus status = new SafetySourceStatus.Builder(
-                context.getString(R.string.unlock_set_unlock_launch_picker_title_profile),
+                context.getString(R.string.unlock_set_unlock_launch_picker_title),
                 screenLockPreferenceDetailsUtils.getSummary(UserHandle.myUserId()),
                 screenLockPreferenceDetailsUtils.isLockPatternSecure()
                         ? SafetySourceStatus.STATUS_LEVEL_OK
diff --git a/tests/robotests/src/com/android/settings/AllInOneTetherSettingsTest.java b/tests/robotests/src/com/android/settings/AllInOneTetherSettingsTest.java
index aec31fc..01ec42e 100644
--- a/tests/robotests/src/com/android/settings/AllInOneTetherSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/AllInOneTetherSettingsTest.java
@@ -33,6 +33,7 @@
 
 import android.content.Context;
 import android.net.ConnectivityManager;
+import android.net.EthernetManager;
 import android.net.TetheringManager;
 import android.net.wifi.SoftApConfiguration;
 import android.net.wifi.WifiManager;
@@ -84,6 +85,8 @@
     private PreferenceScreen mPreferenceScreen;
     @Mock
     private PreferenceGroup mWifiTetherGroup;
+    @Mock
+    private EthernetManager mEthernetManager;
 
     @Before
     public void setUp() {
@@ -95,6 +98,7 @@
                 .when(mContext).getSystemService(Context.CONNECTIVITY_SERVICE);
         doReturn(mTetheringManager)
                 .when(mContext).getSystemService(Context.TETHERING_SERVICE);
+        doReturn(mEthernetManager).when(mContext).getSystemService(EthernetManager.class);
         doReturn(WIFI_REGEXS).when(mTetheringManager).getTetherableWifiRegexs();
         doReturn(USB_REGEXS).when(mTetheringManager).getTetherableUsbRegexs();
         doReturn(BT_REGEXS).when(mTetheringManager).getTetherableBluetoothRegexs();
diff --git a/tests/robotests/src/com/android/settings/network/EthernetTetherPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/EthernetTetherPreferenceControllerTest.java
index bf4811b..68d80d4 100644
--- a/tests/robotests/src/com/android/settings/network/EthernetTetherPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/network/EthernetTetherPreferenceControllerTest.java
@@ -19,6 +19,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -65,10 +66,9 @@
         mPreference = spy(SwitchPreference.class);
         when(mContext.getSystemService(Context.TETHERING_SERVICE)).thenReturn(mTetheringManager);
         when(mTetheringManager.getTetherableIfaces()).thenReturn(new String[]{ETHERNET_REGEX});
-        when(mContext.getSystemService(Context.ETHERNET_SERVICE)).thenReturn(mEthernetManager);
+        when(mContext.getSystemService(EthernetManager.class)).thenReturn(mEthernetManager);
         mController = new EthernetTetherPreferenceController(mContext, "ethernet");
         mController.setTetherEnabler(mTetherEnabler);
-        ReflectionHelpers.setField(mController, "mEthernetRegex", ETHERNET_REGEX);
         ReflectionHelpers.setField(mController, "mPreference", mPreference);
     }
 
@@ -77,7 +77,8 @@
     public void lifecycle_shouldRegisterReceiverOnStart() {
         mController.onStart();
 
-        verify(mEthernetManager).addListener(eq(mController.mEthernetListener));
+        verify(mEthernetManager).addInterfaceStateListener(any(),
+                eq(mController.mEthernetListener));
     }
 
     @Test
@@ -95,11 +96,10 @@
     @Test
     public void lifecycle_shouldUnregisterReceiverOnStop() {
         mController.onStart();
-        EthernetManager.Listener listener = mController.mEthernetListener;
+        EthernetManager.InterfaceStateListener listener = mController.mEthernetListener;
         mController.onStop();
 
-        verify(mEthernetManager).removeListener(eq(listener));
-        assertThat(mController.mEthernetListener).isNull();
+        verify(mEthernetManager).removeInterfaceStateListener(eq(listener));
     }
 
     @Test
@@ -110,8 +110,11 @@
 
     @Test
     public void shouldShow_noEthernetInterface() {
-        ReflectionHelpers.setField(mController, "mEthernetRegex", "");
-        assertThat(mController.shouldShow()).isFalse();
+        when(mContext.getSystemService(EthernetManager.class)).thenReturn(null);
+
+        final EthernetTetherPreferenceController controller =
+                new EthernetTetherPreferenceController(mContext, "ethernet");
+        assertThat(controller.shouldShow()).isFalse();
     }
 
     @Test
diff --git a/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/BridgedAppsPreferenceControllerTest.java b/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/BridgedAppsPreferenceControllerTest.java
index 90b8927..38c605c 100644
--- a/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/BridgedAppsPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/BridgedAppsPreferenceControllerTest.java
@@ -30,7 +30,6 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.VersionedPackage;
-import android.graphics.drawable.Drawable;
 import android.os.Looper;
 import android.service.notification.NotificationListenerFilter;
 import android.util.ArraySet;
@@ -65,6 +64,7 @@
     PreferenceScreen mScreen;
     @Mock
     ApplicationsState mAppState;
+
     private ApplicationsState.AppEntry mAppEntry;
     private ApplicationsState.AppEntry mAppEntry2;
 
@@ -92,8 +92,6 @@
 
         mAppEntry.info = ai;
         mAppEntry.label = "hi";
-        Drawable icon = mock(Drawable.class);
-        mAppEntry.icon = icon;
 
         mController = new BridgedAppsPreferenceController(mContext, "key");
         mController.setCn(mCn);
@@ -167,7 +165,7 @@
 
         assertThat(actual.isChecked()).isTrue();
         assertThat(actual.getTitle()).isEqualTo("hi");
-        assertThat(actual.getIcon()).isEqualTo(mAppEntry.icon);
+        assertThat(actual.getIcon()).isNotNull();
     }
 
     @Test
diff --git a/tests/unit/src/com/android/settings/safetycenter/LockScreenSafetySourceTest.java b/tests/unit/src/com/android/settings/safetycenter/LockScreenSafetySourceTest.java
index 4b21408..64b9692 100644
--- a/tests/unit/src/com/android/settings/safetycenter/LockScreenSafetySourceTest.java
+++ b/tests/unit/src/com/android/settings/safetycenter/LockScreenSafetySourceTest.java
@@ -108,7 +108,7 @@
         assertThat(safetySourceStatus.getTitle().toString())
                 .isEqualTo(ResourcesUtils.getResourcesString(
                         mApplicationContext,
-                        "unlock_set_unlock_launch_picker_title_profile"));
+                        "unlock_set_unlock_launch_picker_title"));
         assertThat(safetySourceStatus.getSummary().toString())
                 .isEqualTo(SUMMARY);
         assertThat(safetySourceStatus.getPendingIntent().getIntent()).isNotNull();