Merge "Relax char length limits and add clarifications"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 8ce83b4..c13e5f3 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1862,35 +1862,43 @@
 
         <!-- Exported for SystemUI to launch into -->
         <activity android:name=".deviceinfo.StorageWizardInit"
-                android:theme="@style/SuwThemeMaterial.Light"
+                android:theme="@style/SetupWizardStorageStyle"
                 android:taskAffinity="com.android.settings.storage_wizard"
                 android:exported="true"
                 android:permission="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
         <activity android:name=".deviceinfo.StorageWizardFormatConfirm"
+                android:theme="@style/SetupWizardStorageStyle"
                 android:taskAffinity="com.android.settings.storage_wizard"
                 android:exported="false" />
         <activity android:name=".deviceinfo.StorageWizardFormatProgress"
+                android:theme="@style/SetupWizardStorageStyle"
                 android:taskAffinity="com.android.settings.storage_wizard"
                 android:exported="false" />
         <activity android:name=".deviceinfo.StorageWizardMigrate"
+                android:theme="@style/SetupWizardStorageStyle"
                 android:taskAffinity="com.android.settings.storage_wizard"
                 android:exported="false" />
         <activity android:name=".deviceinfo.StorageWizardMigrateConfirm"
+                android:theme="@style/SetupWizardStorageStyle"
                 android:taskAffinity="com.android.settings.storage_wizard"
                 android:exported="false" />
         <activity android:name=".deviceinfo.StorageWizardMigrateProgress"
+                android:theme="@style/SetupWizardStorageStyle"
                 android:taskAffinity="com.android.settings.storage_wizard"
                 android:exported="true"
                 android:permission="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
         <activity android:name=".deviceinfo.StorageWizardReady"
+                android:theme="@style/SetupWizardStorageStyle"
                 android:taskAffinity="com.android.settings.storage_wizard"
                 android:exported="true"
                 android:permission="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
 
         <activity android:name=".deviceinfo.StorageWizardMoveConfirm"
+                android:theme="@style/SetupWizardStorageStyle"
                 android:taskAffinity="com.android.settings.storage_wizard"
                 android:exported="false" />
         <activity android:name=".deviceinfo.StorageWizardMoveProgress"
+                android:theme="@style/SetupWizardStorageStyle"
                 android:taskAffinity="com.android.settings.storage_wizard"
                 android:exported="true"
                 android:permission="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
diff --git a/res/drawable/ic_perm_device_information_red_24dp.xml b/res/drawable/ic_perm_device_information_red_24dp.xml
new file mode 100644
index 0000000..135e212
--- /dev/null
+++ b/res/drawable/ic_perm_device_information_red_24dp.xml
@@ -0,0 +1,27 @@
+<!--
+    Copyright (C) 2017 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorError">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M13,7h-2v2h2L13,7zM13,11h-2v6h2v-6zM17,1.01L7,1c-1.1,0 -2,0.9 -2,2v18c0,
+        1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2L19,3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19L7,19L7,5h10v14z"/>
+</vector>
diff --git a/res/layout/storage_wizard_footer.xml b/res/layout/storage_wizard_footer.xml
new file mode 100644
index 0000000..f8384dc
--- /dev/null
+++ b/res/layout/storage_wizard_footer.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2017 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
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    style="@style/SuwGlifButtonBar"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <Space
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_weight="1" />
+
+    <Button
+        android:id="@+id/storage_next_button"
+        style="@style/SuwGlifButton.Primary"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/suw_next_button_label" />
+
+</LinearLayout>
diff --git a/res/layout/storage_wizard_generic.xml b/res/layout/storage_wizard_generic.xml
index 3050c67..b6aab28 100644
--- a/res/layout/storage_wizard_generic.xml
+++ b/res/layout/storage_wizard_generic.xml
@@ -14,13 +14,13 @@
      limitations under the License.
 -->
 
-<com.android.setupwizardlib.SetupWizardLayout
+<com.android.setupwizardlib.GlifLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/setup_wizard_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    app:suwBackgroundTile="@drawable/bg_tile_teal">
+    app:suwFooter="@layout/storage_wizard_footer">
 
     <LinearLayout
         style="@style/SuwContentFrame"
@@ -49,4 +49,4 @@
 
     </LinearLayout>
 
-</com.android.setupwizardlib.SetupWizardLayout>
+</com.android.setupwizardlib.GlifLayout>
diff --git a/res/layout/storage_wizard_init.xml b/res/layout/storage_wizard_init.xml
index 638bfe8..5fc298e 100644
--- a/res/layout/storage_wizard_init.xml
+++ b/res/layout/storage_wizard_init.xml
@@ -14,13 +14,13 @@
      limitations under the License.
 -->
 
-<com.android.setupwizardlib.SetupWizardLayout
+<com.android.setupwizardlib.GlifLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/setup_wizard_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    app:suwBackgroundTile="@drawable/bg_tile_teal">
+    app:suwFooter="@layout/storage_wizard_footer">
 
     <LinearLayout
         style="@style/SuwContentFrame"
@@ -66,4 +66,4 @@
 
     </LinearLayout>
 
-</com.android.setupwizardlib.SetupWizardLayout>
+</com.android.setupwizardlib.GlifLayout>
diff --git a/res/layout/storage_wizard_migrate.xml b/res/layout/storage_wizard_migrate.xml
index 6d041fa..a1c1168 100644
--- a/res/layout/storage_wizard_migrate.xml
+++ b/res/layout/storage_wizard_migrate.xml
@@ -14,13 +14,13 @@
      limitations under the License.
 -->
 
-<com.android.setupwizardlib.SetupWizardLayout
+<com.android.setupwizardlib.GlifLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/setup_wizard_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    app:suwBackgroundTile="@drawable/bg_tile_teal">
+    app:suwFooter="@layout/storage_wizard_footer">
 
     <LinearLayout
         style="@style/SuwContentFrame"
@@ -58,4 +58,4 @@
 
     </LinearLayout>
 
-</com.android.setupwizardlib.SetupWizardLayout>
+</com.android.setupwizardlib.GlifLayout>
diff --git a/res/layout/storage_wizard_progress.xml b/res/layout/storage_wizard_progress.xml
index e3b37f8..dbeaf66 100644
--- a/res/layout/storage_wizard_progress.xml
+++ b/res/layout/storage_wizard_progress.xml
@@ -14,13 +14,13 @@
      limitations under the License.
 -->
 
-<com.android.setupwizardlib.SetupWizardLayout
+<com.android.setupwizardlib.GlifLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/setup_wizard_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    app:suwBackgroundTile="@drawable/bg_tile_teal">
+    app:suwFooter="@layout/storage_wizard_footer">
 
     <LinearLayout
         style="@style/SuwContentFrame"
@@ -54,4 +54,4 @@
 
     </LinearLayout>
 
-</com.android.setupwizardlib.SetupWizardLayout>
+</com.android.setupwizardlib.GlifLayout>
diff --git a/res/layout/storage_wizard_ready.xml b/res/layout/storage_wizard_ready.xml
index d53038d..043b89f 100644
--- a/res/layout/storage_wizard_ready.xml
+++ b/res/layout/storage_wizard_ready.xml
@@ -14,13 +14,13 @@
      limitations under the License.
 -->
 
-<com.android.setupwizardlib.SetupWizardLayout
+<com.android.setupwizardlib.GlifLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/setup_wizard_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    app:suwBackgroundTile="@drawable/bg_tile_teal">
+    app:suwFooter="@layout/storage_wizard_footer">
 
     <LinearLayout
         style="@style/SuwContentFrame"
@@ -39,4 +39,4 @@
 
     </LinearLayout>
 
-</com.android.setupwizardlib.SetupWizardLayout>
+</com.android.setupwizardlib.GlifLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index e1bcc36..08e992d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4755,6 +4755,10 @@
     <string name="battery_tip_summary_title">Battery is in good shape</string>
     <!-- Summary for the battery summary tip [CHAR LIMIT=NONE] -->
     <string name="battery_tip_summary_summary">Apps are behaving normally</string>
+    <!-- Title for the low battery tip [CHAR LIMIT=NONE] -->
+    <string name="battery_tip_low_battery_title">Low battery capacity</string>
+    <!-- Summary for the low battery tip [CHAR LIMIT=NONE] -->
+    <string name="battery_tip_low_battery_summary">Battery can\'t provide good battery life</string>
 
     <!-- Title for force stop dialog [CHAR LIMIT=30] -->
     <string name="dialog_stop_title">Stop app?</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index c68eb72..a07bade 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -362,8 +362,7 @@
         <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
 
-    <style name="SetupWizardStorageStyle" parent="@style/SuwThemeMaterial.Light">
-        <item name="android:colorAccent">#ff009688</item>
+    <style name="SetupWizardStorageStyle" parent="@style/SuwThemeGlif.Light">
     </style>
 
     <style name="PreviewPagerPageIndicator">
diff --git a/res/xml/location_settings.xml b/res/xml/location_settings.xml
index 96fe905..f8ac192 100644
--- a/res/xml/location_settings.xml
+++ b/res/xml/location_settings.xml
@@ -20,19 +20,9 @@
         android:title="@string/location_settings_title"
         settings:keywords="@string/keywords_location">
 
-        <Preference
-            android:key="location_mode"
-            android:title="@string/location_mode_title"
-            settings:keywords="@string/keywords_location_mode"
-            android:summary="@string/summary_placeholder" />
-
-        <!-- This preference category gets removed if there is no managed profile -->
-        <com.android.settingslib.RestrictedSwitchPreference
-            android:key="managed_profile_location_switch"
-            android:title="@string/managed_profile_location_switch_title"
-            settings:useAdminDisabledSummary="true"
-            android:enabled="false"
-            android:selectable="true" />
+        <PreferenceCategory
+            android:key="recent_location_requests"
+            android:title="@string/location_category_recent_location_requests"/>
 
         <!-- This preference category gets removed if new_recent_location_ui is disabled -->
         <Preference
@@ -49,12 +39,21 @@
             android:title="@string/location_scanning_screen_title"
             android:fragment="com.android.settings.location.ScanningSettings"/>
 
-        <PreferenceCategory
-            android:key="recent_location_requests"
-            android:title="@string/location_category_recent_location_requests" />
+        <!-- This preference category gets removed if there is no managed profile -->
+        <com.android.settingslib.RestrictedSwitchPreference
+            android:key="managed_profile_location_switch"
+            android:title="@string/managed_profile_location_switch_title"
+            settings:useAdminDisabledSummary="true"
+            android:enabled="false"
+            android:selectable="true" />
 
         <PreferenceCategory
             android:key="location_services"
             android:title="@string/location_category_location_services" />
 
+        <Preference
+            android:key="location_mode"
+            android:title="@string/location_mode_title"
+            settings:keywords="@string/keywords_location_mode"
+            android:summary="@string/summary_placeholder" />
 </PreferenceScreen>
diff --git a/src/com/android/settings/applications/VrListenerSettings.java b/src/com/android/settings/applications/VrListenerSettings.java
index e40e3f9..ea88ae4 100644
--- a/src/com/android/settings/applications/VrListenerSettings.java
+++ b/src/com/android/settings/applications/VrListenerSettings.java
@@ -27,20 +27,16 @@
 
 public class VrListenerSettings extends ManagedServiceSettings {
     private static final String TAG = VrListenerSettings.class.getSimpleName();
-    private static final Config CONFIG = getVrListenerConfig();
-
-    private static final Config getVrListenerConfig() {
-        final Config c = new Config();
-        c.tag = TAG;
-        c.setting = Settings.Secure.ENABLED_VR_LISTENERS;
-        c.intentAction = VrListenerService.SERVICE_INTERFACE;
-        c.permission = android.Manifest.permission.BIND_VR_LISTENER_SERVICE;
-        c.noun = "vr listener";
-        c.warningDialogTitle = R.string.vr_listener_security_warning_title;
-        c.warningDialogSummary = R.string.vr_listener_security_warning_summary;
-        c.emptyText = R.string.no_vr_listeners;
-        return c;
-    }
+    private static final Config CONFIG = new Config.Builder()
+            .setTag(TAG)
+            .setSetting(Settings.Secure.ENABLED_VR_LISTENERS)
+            .setIntentAction(VrListenerService.SERVICE_INTERFACE)
+            .setPermission(android.Manifest.permission.BIND_VR_LISTENER_SERVICE)
+            .setNoun("vr listener")
+            .setWarningDialogTitle(R.string.vr_listener_security_warning_title)
+            .setWarningDialogSummary(R.string.vr_listener_security_warning_summary)
+            .setEmptyText(R.string.no_vr_listeners)
+            .build();
 
     @Override
     protected Config getConfig() {
diff --git a/src/com/android/settings/deviceinfo/StorageWizardBase.java b/src/com/android/settings/deviceinfo/StorageWizardBase.java
index c2ea2d2..5b48666 100644
--- a/src/com/android/settings/deviceinfo/StorageWizardBase.java
+++ b/src/com/android/settings/deviceinfo/StorageWizardBase.java
@@ -20,7 +20,9 @@
 
 import android.annotation.LayoutRes;
 import android.app.Activity;
-import android.graphics.Color;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.os.storage.DiskInfo;
@@ -30,16 +32,12 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
 import android.widget.Button;
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
 import com.android.settings.R;
-import com.android.setupwizardlib.SetupWizardLayout;
-import com.android.setupwizardlib.view.Illustration;
+import com.android.setupwizardlib.GlifLayout;
 
 import java.text.NumberFormat;
 import java.util.List;
@@ -51,8 +49,7 @@
     protected VolumeInfo mVolume;
     protected DiskInfo mDisk;
 
-    private View mCustomNav;
-    private Button mCustomNext;
+    private Button mNext;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -72,8 +69,6 @@
             mDisk = mVolume.getDisk();
         }
 
-        setTheme(R.style.SetupWizardStorageStyle);
-
         if (mDisk != null) {
             mStorage.registerListener(mStorageListener);
         }
@@ -83,53 +78,13 @@
     public void setContentView(@LayoutRes int layoutResID) {
         super.setContentView(layoutResID);
 
-        // Our wizard is a unique flower, so it has custom buttons
-        final ViewGroup navParent = (ViewGroup) findViewById(R.id.suw_layout_navigation_bar)
-                .getParent();
-        mCustomNav = getLayoutInflater().inflate(R.layout.storage_wizard_navigation,
-                navParent, false);
-
-        mCustomNext = (Button) mCustomNav.findViewById(R.id.suw_navbar_next);
-        mCustomNext.setOnClickListener(new View.OnClickListener() {
+        mNext = (Button) findViewById(R.id.storage_next_button);
+        mNext.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 onNavigateNext();
             }
         });
-
-        // Swap our custom navigation bar into place
-        for (int i = 0; i < navParent.getChildCount(); i++) {
-            if (navParent.getChildAt(i).getId() == R.id.suw_layout_navigation_bar) {
-                navParent.removeViewAt(i);
-                navParent.addView(mCustomNav, i);
-                break;
-            }
-        }
-    }
-
-    @Override
-    protected void onPostCreate(Bundle savedInstanceState) {
-        super.onPostCreate(savedInstanceState);
-
-        final Window window = getWindow();
-        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS |
-                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
-                WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
-        window.setStatusBarColor(Color.TRANSPARENT);
-
-        mCustomNav.setSystemUiVisibility(
-                View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
-
-        final View scrollView = findViewById(R.id.suw_bottom_scroll_view);
-        scrollView.setVerticalFadingEdgeEnabled(true);
-        scrollView.setFadingEdgeLength(scrollView.getVerticalFadingEdgeLength() * 2);
-
-        if (findViewById(R.id.suw_layout_decor) instanceof Illustration) {
-            // Our header illustration already have padding baked in
-            final View title = findViewById(R.id.suw_layout_title);
-            title.setPadding(title.getPaddingLeft(), 0, title.getPaddingRight(),
-                    title.getPaddingBottom());
-        }
     }
 
     @Override
@@ -139,11 +94,11 @@
     }
 
     protected Button getNextButton() {
-        return mCustomNext;
+        return mNext;
     }
 
-    protected SetupWizardLayout getSetupWizardLayout() {
-        return (SetupWizardLayout) findViewById(R.id.setup_wizard_layout);
+    protected GlifLayout getGlifLayout() {
+        return (GlifLayout) findViewById(R.id.setup_wizard_layout);
     }
 
     protected ProgressBar getProgressBar() {
@@ -158,7 +113,7 @@
 
     protected void setHeaderText(int resId, String... args) {
         final CharSequence headerText = TextUtils.expandTemplate(getText(resId), args);
-        getSetupWizardLayout().setHeaderText(headerText);
+        getGlifLayout().setHeaderText(headerText);
         setTitle(headerText);
     }
 
@@ -178,27 +133,16 @@
     protected static final int ILLUSTRATION_PORTABLE = 2;
 
     protected void setIllustrationType(int type) {
-        switch (type) {
-            case ILLUSTRATION_SETUP:
-                getSetupWizardLayout().setIllustration(
-                        R.drawable.bg_setup_header,
-                        R.drawable.bg_header_horizontal_tile);
-                break;
-            case ILLUSTRATION_INTERNAL:
-                getSetupWizardLayout().setIllustration(
-                        R.drawable.bg_internal_storage_header,
-                        R.drawable.bg_header_horizontal_tile);
-                break;
-            case ILLUSTRATION_PORTABLE:
-                getSetupWizardLayout().setIllustration(
-                        R.drawable.bg_portable_storage_header,
-                        R.drawable.bg_header_horizontal_tile);
-                break;
-        }
+        // TODO: map type to updated icons once provided by UX
+        TypedArray array = obtainStyledAttributes(new int[] {android.R.attr.colorAccent});
+        Drawable icon = getDrawable(com.android.internal.R.drawable.ic_sd_card_48dp).mutate();
+        icon.setTint(array.getColor(0, 0));
+        array.recycle();
+        getGlifLayout().setIcon(icon);
     }
 
     protected void setKeepScreenOn(boolean keepScreenOn) {
-        getSetupWizardLayout().setKeepScreenOn(keepScreenOn);
+        getGlifLayout().setKeepScreenOn(keepScreenOn);
     }
 
     public void onNavigateNext() {
diff --git a/src/com/android/settings/display/TimeoutPreferenceController.java b/src/com/android/settings/display/TimeoutPreferenceController.java
index 3e1d86a..1d86999 100644
--- a/src/com/android/settings/display/TimeoutPreferenceController.java
+++ b/src/com/android/settings/display/TimeoutPreferenceController.java
@@ -65,7 +65,7 @@
             final RestrictedLockUtils.EnforcedAdmin admin =
                     RestrictedLockUtils.checkIfMaximumTimeToLockIsSet(mContext);
             final long maxTimeout =
-                    dpm.getMaximumTimeToLockForUserAndProfiles(UserHandle.myUserId());
+                    dpm.getMaximumTimeToLock(null /* admin */, UserHandle.myUserId());
             timeoutListPreference.removeUnusableTimeouts(maxTimeout, admin);
         }
         updateTimeoutPreferenceDescription(timeoutListPreference, currentTimeout);
diff --git a/src/com/android/settings/fuelgauge/FakeUid.java b/src/com/android/settings/fuelgauge/FakeUid.java
index 2b5afe1..cbd9c3f 100644
--- a/src/com/android/settings/fuelgauge/FakeUid.java
+++ b/src/com/android/settings/fuelgauge/FakeUid.java
@@ -361,4 +361,14 @@
     public long[] getScreenOffCpuFreqTimes(int which) {
         return null;
     }
+
+    @Override
+    public long[] getCpuFreqTimes(int procState, int which) {
+        return null;
+    }
+
+    @Override
+    public long[] getScreenOffCpuFreqTimes(int procState, int which) {
+        return null;
+    }
 }
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
index b8cb6c4..9c3f48c 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
@@ -17,13 +17,21 @@
 package com.android.settings.fuelgauge.batterytip;
 
 import android.content.Context;
+import android.support.annotation.VisibleForTesting;
 
 import com.android.internal.os.BatteryStatsHelper;
+import com.android.settings.fuelgauge.BatteryInfo;
+import com.android.settings.fuelgauge.BatteryUtils;
+import com.android.settings.fuelgauge.batterytip.detectors.BatteryTipDetector;
+import com.android.settings.fuelgauge.batterytip.detectors.LowBatteryDetector;
+import com.android.settings.fuelgauge.batterytip.detectors.SummaryDetector;
 import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
+import com.android.settings.fuelgauge.batterytip.tips.LowBatteryTip;
 import com.android.settings.fuelgauge.batterytip.tips.SummaryTip;
 import com.android.settingslib.utils.AsyncLoader;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -36,18 +44,31 @@
     private static final boolean USE_FAKE_DATA = false;
 
     private BatteryStatsHelper mBatteryStatsHelper;
+    private BatteryUtils mBatteryUtils;
+    @VisibleForTesting
+    int mVisibleTips;
 
     public BatteryTipLoader(Context context, BatteryStatsHelper batteryStatsHelper) {
         super(context);
         mBatteryStatsHelper = batteryStatsHelper;
+        mBatteryUtils = BatteryUtils.getInstance(context);
     }
 
     @Override
     public List<BatteryTip> loadInBackground() {
-        List<BatteryTip> tips = new ArrayList<>();
+        if (USE_FAKE_DATA) {
+            return getFakeData();
+        }
+        final List<BatteryTip> tips = new ArrayList<>();
+        final BatteryTipPolicy policy = new BatteryTipPolicy(getContext());
+        final BatteryInfo batteryInfo = mBatteryUtils.getBatteryInfo(mBatteryStatsHelper, TAG);
+        mVisibleTips = 0;
 
-        //TODO(b/70570352): add battery tip detectors
-        tips.add(new SummaryTip(BatteryTip.StateType.NEW));
+        addBatteryTipFromDetector(tips, new LowBatteryDetector(policy, batteryInfo));
+        // Add summary detector at last since it need other detectors to update the mVisibleTips
+        addBatteryTipFromDetector(tips, new SummaryDetector(policy, mVisibleTips));
+
+        Collections.sort(tips);
         return tips;
     }
 
@@ -55,4 +76,20 @@
     protected void onDiscardResult(List<BatteryTip> result) {
     }
 
+    private List<BatteryTip> getFakeData() {
+        final List<BatteryTip> tips = new ArrayList<>();
+        tips.add(new SummaryTip(BatteryTip.StateType.NEW));
+        tips.add(new LowBatteryTip(BatteryTip.StateType.NEW));
+
+        return tips;
+    }
+
+    @VisibleForTesting
+    void addBatteryTipFromDetector(final List<BatteryTip> tips,
+            final BatteryTipDetector detector) {
+        final BatteryTip batteryTip = detector.detect();
+        mVisibleTips += batteryTip.isVisible() ? 1 : 0;
+        tips.add(batteryTip);
+    }
+
 }
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java
new file mode 100644
index 0000000..ac5072d
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2017 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.fuelgauge.batterytip;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.support.annotation.VisibleForTesting;
+import android.util.KeyValueListParser;
+import android.util.Log;
+
+/**
+ * Class to store the policy for battery tips, which comes from
+ * {@link Settings.Global}
+ */
+public class BatteryTipPolicy {
+    public static final String TAG = "BatteryTipPolicy";
+
+    private static final String KEY_BATTERY_TIP_ENABLED = "battery_tip_enabled";
+    private static final String KEY_SUMMARY_ENABLED = "summary_enabled";
+    private static final String KEY_BATTERY_SAVER_TIP_ENABLED = "battery_saver_tip_enabled";
+    private static final String KEY_HIGH_USAGE_ENABLED = "high_usage_enabled";
+    private static final String KEY_HIGH_USAGE_APP_COUNT = "high_usage_app_count";
+    private static final String KEY_APP_RESTRICTION_ENABLED = "app_restriction_enabled";
+    private static final String KEY_REDUCED_BATTERY_ENABLED = "reduced_battery_enabled";
+    private static final String KEY_REDUCED_BATTERY_PERCENT = "reduced_battery_percent";
+    private static final String KEY_LOW_BATTERY_ENABLED = "low_battery_enabled";
+    private static final String KEY_LOW_BATTERY_HOUR = "low_battery_hour";
+
+    /**
+     * {@code true} if general battery tip is enabled
+     *
+     * @see Settings.Global#BATTERY_TIP_CONSTANTS
+     * @see #KEY_BATTERY_TIP_ENABLED
+     */
+    public final boolean batteryTipEnabled;
+
+    /**
+     * {@code true} if summary tip is enabled
+     *
+     * @see Settings.Global#BATTERY_TIP_CONSTANTS
+     * @see #KEY_SUMMARY_ENABLED
+     */
+    public final boolean summaryEnabled;
+
+    /**
+     * {@code true} if battery saver tip is enabled
+     *
+     * @see Settings.Global#BATTERY_TIP_CONSTANTS
+     * @see #KEY_BATTERY_SAVER_TIP_ENABLED
+     */
+    public final boolean batterySaverTipEnabled;
+
+    /**
+     * {@code true} if high usage tip is enabled
+     *
+     * @see Settings.Global#BATTERY_TIP_CONSTANTS
+     * @see #KEY_HIGH_USAGE_ENABLED
+     */
+    public final boolean highUsageEnabled;
+
+    /**
+     * The maximum number of apps shown in high usage
+     *
+     * @see Settings.Global#BATTERY_TIP_CONSTANTS
+     * @see #KEY_HIGH_USAGE_APP_COUNT
+     */
+    public final int highUsageAppCount;
+
+    /**
+     * {@code true} if app restriction tip is enabled
+     *
+     * @see Settings.Global#BATTERY_TIP_CONSTANTS
+     * @see #KEY_APP_RESTRICTION_ENABLED
+     */
+    public final boolean appRestrictionEnabled;
+
+    /**
+     * {@code true} if reduced battery tip is enabled
+     *
+     * @see Settings.Global#BATTERY_TIP_CONSTANTS
+     * @see #KEY_REDUCED_BATTERY_ENABLED
+     */
+    public final boolean reducedBatteryEnabled;
+
+    /**
+     * The percentage of reduced battery to trigger the tip(e.g. 50%)
+     *
+     * @see Settings.Global#BATTERY_TIP_CONSTANTS
+     * @see #KEY_REDUCED_BATTERY_PERCENT
+     */
+    public final int reducedBatteryPercent;
+
+    /**
+     * {@code true} if low battery tip is enabled
+     *
+     * @see Settings.Global#BATTERY_TIP_CONSTANTS
+     * @see #KEY_LOW_BATTERY_ENABLED
+     */
+    public final boolean lowBatteryEnabled;
+
+    /**
+     * Remaining battery hour to trigger the tip(e.g. 16 hours)
+     *
+     * @see Settings.Global#BATTERY_TIP_CONSTANTS
+     * @see #KEY_LOW_BATTERY_HOUR
+     */
+    public final int lowBatteryHour;
+
+    private final KeyValueListParser mParser;
+
+    public BatteryTipPolicy(Context context) {
+        this(context, new KeyValueListParser(','));
+    }
+
+    @VisibleForTesting
+    BatteryTipPolicy(Context context, KeyValueListParser parser) {
+        mParser = parser;
+        final String value = Settings.Global.getString(context.getContentResolver(),
+                Settings.Global.BATTERY_TIP_CONSTANTS);
+
+        try {
+            mParser.setString(value);
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "Bad battery tip constants");
+        }
+
+        batteryTipEnabled = mParser.getBoolean(KEY_BATTERY_TIP_ENABLED, true);
+        summaryEnabled = mParser.getBoolean(KEY_SUMMARY_ENABLED, true);
+        batterySaverTipEnabled = mParser.getBoolean(KEY_BATTERY_SAVER_TIP_ENABLED, true);
+        highUsageEnabled = mParser.getBoolean(KEY_HIGH_USAGE_ENABLED, true);
+        highUsageAppCount = mParser.getInt(KEY_HIGH_USAGE_APP_COUNT, 3);
+        appRestrictionEnabled = mParser.getBoolean(KEY_APP_RESTRICTION_ENABLED, true);
+        reducedBatteryEnabled = mParser.getBoolean(KEY_REDUCED_BATTERY_ENABLED, true);
+        reducedBatteryPercent = mParser.getInt(KEY_REDUCED_BATTERY_PERCENT, 50);
+        lowBatteryEnabled = mParser.getBoolean(KEY_LOW_BATTERY_ENABLED, true);
+        lowBatteryHour = mParser.getInt(KEY_LOW_BATTERY_HOUR, 16);
+    }
+
+}
diff --git a/src/com/android/settings/fuelgauge/batterytip/detectors/BatteryTipDetector.java b/src/com/android/settings/fuelgauge/batterytip/detectors/BatteryTipDetector.java
new file mode 100644
index 0000000..cb38e40
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterytip/detectors/BatteryTipDetector.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 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.fuelgauge.batterytip.detectors;
+
+import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
+
+public interface BatteryTipDetector {
+    /**
+     * Detect and update the status of {@link BatteryTip}
+     *
+     * @return a not null {@link BatteryTip}
+     */
+    BatteryTip detect();
+}
diff --git a/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetector.java b/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetector.java
new file mode 100644
index 0000000..2a6302e
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetector.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017 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.fuelgauge.batterytip.detectors;
+
+import android.text.format.DateUtils;
+
+import com.android.settings.fuelgauge.BatteryInfo;
+import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
+import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
+import com.android.settings.fuelgauge.batterytip.tips.LowBatteryTip;
+
+/**
+ * Detect whether the battery is too low
+ */
+public class LowBatteryDetector implements BatteryTipDetector {
+    private BatteryInfo mBatteryInfo;
+    private BatteryTipPolicy mPolicy;
+
+    public LowBatteryDetector(BatteryTipPolicy policy, BatteryInfo batteryInfo) {
+        mPolicy = policy;
+        mBatteryInfo = batteryInfo;
+    }
+
+    @Override
+    public BatteryTip detect() {
+        // Show it if battery life is less than mPolicy.lowBatteryHour
+        final boolean isShown = mPolicy.lowBatteryEnabled && mBatteryInfo.discharging
+                && mBatteryInfo.remainingTimeUs < mPolicy.lowBatteryHour * DateUtils.HOUR_IN_MILLIS;
+        return new LowBatteryTip(
+                isShown ? BatteryTip.StateType.NEW : BatteryTip.StateType.INVISIBLE);
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/batterytip/detectors/SummaryDetector.java b/src/com/android/settings/fuelgauge/batterytip/detectors/SummaryDetector.java
new file mode 100644
index 0000000..8c1783b
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterytip/detectors/SummaryDetector.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 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.fuelgauge.batterytip.detectors;
+
+import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
+import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
+import com.android.settings.fuelgauge.batterytip.tips.SummaryTip;
+
+/**
+ * Detector whether to show summary tip. This detector should be executed as the last
+ * {@link BatteryTipDetector} since it need the most up-to-date {@code visibleTips}
+ */
+public class SummaryDetector implements BatteryTipDetector {
+    private BatteryTipPolicy mPolicy;
+    private int mVisibleTips;
+
+    public SummaryDetector(BatteryTipPolicy policy, int visibleTips) {
+        mPolicy = policy;
+        mVisibleTips = visibleTips;
+    }
+
+    @Override
+    public BatteryTip detect() {
+        // Show it if there is no other tips shown
+        final int state = mPolicy.summaryEnabled && mVisibleTips == 0
+                ? BatteryTip.StateType.NEW
+                : BatteryTip.StateType.INVISIBLE;
+        return new SummaryTip(state);
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
index e633272..17e395e 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java
@@ -31,7 +31,7 @@
  * Each {@link BatteryTip} contains basic data(e.g. title, summary, icon) as well as the
  * pre-defined action(e.g. turn on battery saver)
  */
-public abstract class BatteryTip {
+public abstract class BatteryTip implements Comparable<BatteryTip> {
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({StateType.NEW,
             StateType.HANDLED,
@@ -114,4 +114,13 @@
     public int getState() {
         return mState;
     }
+
+    public boolean isVisible() {
+        return mState != StateType.INVISIBLE;
+    }
+
+    @Override
+    public int compareTo(BatteryTip o) {
+        return mType - o.mType;
+    }
 }
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTip.java
new file mode 100644
index 0000000..8605fbb
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTip.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 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.fuelgauge.batterytip.tips;
+
+import android.app.Dialog;
+import android.content.Context;
+
+import com.android.settings.R;
+
+/**
+ * Tip to show current battery life is short
+ */
+public class LowBatteryTip extends BatteryTip {
+
+    public LowBatteryTip(@StateType int state) {
+        mShowDialog = false;
+        mState = state;
+        mType = TipType.LOW_BATTERY;
+    }
+
+    @Override
+    public CharSequence getTitle(Context context) {
+        return context.getString(R.string.battery_tip_low_battery_title);
+    }
+
+    @Override
+    public CharSequence getSummary(Context context) {
+        return context.getString(R.string.battery_tip_low_battery_summary);
+    }
+
+    @Override
+    public int getIconId() {
+        return R.drawable.ic_perm_device_information_red_24dp;
+    }
+
+    @Override
+    public void updateState(BatteryTip tip) {
+        mState = tip.mState;
+    }
+
+    @Override
+    public void action() {
+        // do nothing
+    }
+
+    @Override
+    public Dialog buildDialog() {
+        //TODO(b/70570352): create the dialog for low battery tip and add test
+        return null;
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/SummaryTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/SummaryTip.java
index ab2a6c3..2a2deab 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/SummaryTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/SummaryTip.java
@@ -29,6 +29,7 @@
     public SummaryTip(@StateType int state) {
         mShowDialog = false;
         mState = state;
+        mType = TipType.SUMMARY;
     }
 
     @Override
diff --git a/src/com/android/settings/location/LocationSettings.java b/src/com/android/settings/location/LocationSettings.java
index 3330421..1279d67 100644
--- a/src/com/android/settings/location/LocationSettings.java
+++ b/src/com/android/settings/location/LocationSettings.java
@@ -73,6 +73,16 @@
     private LocationSwitchBarController mSwitchBarController;
 
     @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        final RecentLocationApps recentLocationApps = new RecentLocationApps(getActivity());
+        int locationRequestsApps = recentLocationApps.getAppList().size();
+        int locationRequestsPrefs = locationRequestsApps == 0 ? 1 : locationRequestsApps;
+        getPreferenceScreen().setInitialExpandedChildrenCount(locationRequestsPrefs + 2);
+    }
+
+
+    @Override
     public int getMetricsCategory() {
         return MetricsEvent.LOCATION;
     }
diff --git a/src/com/android/settings/notification/NotificationAccessSettings.java b/src/com/android/settings/notification/NotificationAccessSettings.java
index af89f49..4180a53 100644
--- a/src/com/android/settings/notification/NotificationAccessSettings.java
+++ b/src/com/android/settings/notification/NotificationAccessSettings.java
@@ -19,8 +19,9 @@
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.Fragment;
+import android.app.NotificationManager;
 import android.content.ComponentName;
-import android.content.DialogInterface;
+import android.content.Context;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.provider.Settings;
@@ -33,22 +34,23 @@
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.utils.ManagedServiceSettings;
 
+/**
+ * Settings screen for managing notification listener permissions
+ */
 public class NotificationAccessSettings extends ManagedServiceSettings {
     private static final String TAG = NotificationAccessSettings.class.getSimpleName();
-    private static final Config CONFIG = getNotificationListenerConfig();
+    private static final Config CONFIG =  new Config.Builder()
+            .setTag(TAG)
+            .setSetting(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS)
+            .setIntentAction(NotificationListenerService.SERVICE_INTERFACE)
+            .setPermission(android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE)
+            .setNoun("notification listener")
+            .setWarningDialogTitle(R.string.notification_listener_security_warning_title)
+            .setWarningDialogSummary(R.string.notification_listener_security_warning_summary)
+            .setEmptyText(R.string.no_notification_listeners)
+            .build();
 
-    private static Config getNotificationListenerConfig() {
-        final Config c = new Config();
-        c.tag = TAG;
-        c.setting = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
-        c.intentAction = NotificationListenerService.SERVICE_INTERFACE;
-        c.permission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
-        c.noun = "notification listener";
-        c.warningDialogTitle = R.string.notification_listener_security_warning_title;
-        c.warningDialogSummary = R.string.notification_listener_security_warning_summary;
-        c.emptyText = R.string.no_notification_listeners;
-        return c;
-    }
+    private NotificationManager mNm;
 
     @Override
     public int getMetricsCategory() {
@@ -56,6 +58,12 @@
     }
 
     @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        mNm = context.getSystemService(NotificationManager.class);
+    }
+
+    @Override
     protected Config getConfig() {
         return CONFIG;
     }
@@ -109,13 +117,10 @@
 
     private static void disable(final NotificationAccessSettings parent, final ComponentName cn) {
         parent.mNm.setNotificationListenerAccessGranted(cn, false);
-        AsyncTask.execute(new Runnable() {
-            @Override
-            public void run() {
-                if (!parent.mNm.isNotificationPolicyAccessGrantedForPackage(
-                        cn.getPackageName())) {
-                    parent.mNm.removeAutomaticZenRules(cn.getPackageName());
-                }
+        AsyncTask.execute(() -> {
+            if (!parent.mNm.isNotificationPolicyAccessGrantedForPackage(
+                    cn.getPackageName())) {
+                parent.mNm.removeAutomaticZenRules(cn.getPackageName());
             }
         });
     }
@@ -153,16 +158,10 @@
                     .setMessage(summary)
                     .setCancelable(true)
                     .setPositiveButton(R.string.notification_listener_disable_warning_confirm,
-                            new DialogInterface.OnClickListener() {
-                                public void onClick(DialogInterface dialog, int id) {
-                                    disable(parent, cn);
-                                }
-                            })
+                            (dialog, id) -> disable(parent, cn))
                     .setNegativeButton(R.string.notification_listener_disable_warning_cancel,
-                            new DialogInterface.OnClickListener() {
-                                public void onClick(DialogInterface dialog, int id) {
-                                    // pass
-                                }
+                            (dialog, id) -> {
+                                // pass
                             })
                     .create();
         }
diff --git a/src/com/android/settings/notification/ZenModeAutomationSettings.java b/src/com/android/settings/notification/ZenModeAutomationSettings.java
index 55d0fca..6ce13c1 100644
--- a/src/com/android/settings/notification/ZenModeAutomationSettings.java
+++ b/src/com/android/settings/notification/ZenModeAutomationSettings.java
@@ -64,12 +64,12 @@
     }
 
     protected static ManagedServiceSettings.Config getConditionProviderConfig() {
-        final ManagedServiceSettings.Config c = new ManagedServiceSettings.Config();
-        c.tag = TAG;
-        c.intentAction = ConditionProviderService.SERVICE_INTERFACE;
-        c.permission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE;
-        c.noun = "condition provider";
-        return c;
+        return new ManagedServiceSettings.Config.Builder()
+                .setTag(TAG)
+                .setIntentAction(ConditionProviderService.SERVICE_INTERFACE)
+                .setPermission(android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE)
+                .setNoun("condition provider")
+                .build();
     }
 
     /**
diff --git a/src/com/android/settings/search/indexing/PreIndexDataCollector.java b/src/com/android/settings/search/indexing/PreIndexDataCollector.java
index a4e1131..63000b4 100644
--- a/src/com/android/settings/search/indexing/PreIndexDataCollector.java
+++ b/src/com/android/settings/search/indexing/PreIndexDataCollector.java
@@ -287,8 +287,14 @@
             String[] projection) {
 
         final ContentResolver resolver = packageContext.getContentResolver();
-        final Cursor cursor = resolver.query(uri, projection, null, null, null);
         final List<String> result = new ArrayList<>();
+        Cursor cursor;
+        try {
+            cursor = resolver.query(uri, projection, null, null, null);
+        } catch (NullPointerException e) {
+            Log.e(TAG, "Exception querying the keys!", e);
+            return result;
+        }
 
         if (cursor == null) {
             Log.w(TAG, "Cannot add index data for Uri: " + uri.toString());
diff --git a/src/com/android/settings/security/screenlock/LockAfterTimeoutPreferenceController.java b/src/com/android/settings/security/screenlock/LockAfterTimeoutPreferenceController.java
index 71c82f0..08e244e 100644
--- a/src/com/android/settings/security/screenlock/LockAfterTimeoutPreferenceController.java
+++ b/src/com/android/settings/security/screenlock/LockAfterTimeoutPreferenceController.java
@@ -107,8 +107,8 @@
         if (mDPM != null) {
             final RestrictedLockUtils.EnforcedAdmin admin =
                     RestrictedLockUtils.checkIfMaximumTimeToLockIsSet(mContext);
-            final long adminTimeout = mDPM
-                    .getMaximumTimeToLockForUserAndProfiles(UserHandle.myUserId());
+            final long adminTimeout =
+                    mDPM.getMaximumTimeToLock(null /* admin */, UserHandle.myUserId());
             final long displayTimeout = Math.max(0,
                     Settings.System.getInt(mContext.getContentResolver(), SCREEN_OFF_TIMEOUT, 0));
             // This setting is a slave to display timeout when a device policy is enforced.
diff --git a/src/com/android/settings/utils/ManagedServiceSettings.java b/src/com/android/settings/utils/ManagedServiceSettings.java
index d488dfe..d222345 100644
--- a/src/com/android/settings/utils/ManagedServiceSettings.java
+++ b/src/com/android/settings/utils/ManagedServiceSettings.java
@@ -21,11 +21,9 @@
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.Fragment;
-import android.app.NotificationManager;
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.DialogInterface;
 import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
@@ -33,8 +31,6 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.support.v14.preference.SwitchPreference;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.Preference.OnPreferenceChangeListener;
 import android.support.v7.preference.PreferenceScreen;
 import android.util.IconDrawableFactory;
 import android.util.Log;
@@ -46,8 +42,8 @@
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
 import com.android.settings.notification.EmptyTextSettings;
 import com.android.settings.widget.AppSwitchPreference;
+import com.android.settingslib.applications.ServiceListing;
 
-import java.util.Collections;
 import java.util.List;
 
 public abstract class ManagedServiceSettings extends EmptyTextSettings {
@@ -57,8 +53,7 @@
     protected Context mContext;
     private PackageManager mPm;
     private DevicePolicyManager mDpm;
-    protected ServiceListing mServiceListing;
-    protected NotificationManager mNm;
+    private ServiceListing mServiceListing;
     private IconDrawableFactory mIconDrawableFactory;
 
     abstract protected Config getConfig();
@@ -74,15 +69,15 @@
         mContext = getActivity();
         mPm = mContext.getPackageManager();
         mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
-        mNm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
         mIconDrawableFactory = IconDrawableFactory.newInstance(mContext);
-        mServiceListing = new ServiceListing(mContext, mConfig);
-        mServiceListing.addCallback(new ServiceListing.Callback() {
-            @Override
-            public void onServicesReloaded(List<ServiceInfo> services) {
-                updateList(services);
-            }
-        });
+        mServiceListing = new ServiceListing.Builder(mContext)
+                .setPermission(mConfig.permission)
+                .setIntentAction(mConfig.intentAction)
+                .setNoun(mConfig.noun)
+                .setSetting(mConfig.setting)
+                .setTag(mConfig.tag)
+                .build();
+        mServiceListing.addCallback(this::updateList);
         setPreferenceScreen(getPreferenceManager().createPreferenceScreen(mContext));
     }
 
@@ -115,7 +110,7 @@
 
         final PreferenceScreen screen = getPreferenceScreen();
         screen.removeAll();
-        Collections.sort(services, new PackageItemInfo.DisplayNameComparator(mPm));
+        services.sort(new PackageItemInfo.DisplayNameComparator(mPm));
         for (ServiceInfo service : services) {
             final ComponentName cn = new ComponentName(service.packageName, service.name);
             CharSequence title = null;
@@ -144,12 +139,9 @@
                             service.packageName, managedProfileId)) {
                 pref.setSummary(R.string.work_profile_notification_access_blocked_summary);
             }
-            pref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
-                @Override
-                public boolean onPreferenceChange(Preference preference, Object newValue) {
-                    final boolean enable = (boolean) newValue;
-                    return setEnabled(cn, summary, enable);
-                }
+            pref.setOnPreferenceChangeListener((preference, newValue) -> {
+                final boolean enable = (boolean) newValue;
+                return setEnabled(cn, summary, enable);
             });
             screen.addPreference(pref);
         }
@@ -188,8 +180,8 @@
     }
 
     public static class ScaryWarningDialogFragment extends InstrumentedDialogFragment {
-        static final String KEY_COMPONENT = "c";
-        static final String KEY_LABEL = "l";
+        private static final String KEY_COMPONENT = "c";
+        private static final String KEY_LABEL = "l";
 
         @Override
         public int getMetricsCategory() {
@@ -222,29 +214,92 @@
                     .setTitle(title)
                     .setCancelable(true)
                     .setPositiveButton(R.string.allow,
-                            new DialogInterface.OnClickListener() {
-                                public void onClick(DialogInterface dialog, int id) {
-                                    parent.enable(cn);
-                                }
-                            })
+                            (dialog, id) -> parent.enable(cn))
                     .setNegativeButton(R.string.deny,
-                            new DialogInterface.OnClickListener() {
-                                public void onClick(DialogInterface dialog, int id) {
-                                    // pass
-                                }
+                            (dialog, id) -> {
+                                // pass
                             })
                     .create();
         }
     }
 
     public static class Config {
-        public String tag;
-        public String setting;
-        public String intentAction;
-        public String permission;
-        public String noun;
-        public int warningDialogTitle;
-        public int warningDialogSummary;
-        public int emptyText;
+        public final String tag;
+        public final String setting;
+        public final String intentAction;
+        public final String permission;
+        public final String noun;
+        public final int warningDialogTitle;
+        public final int warningDialogSummary;
+        public final int emptyText;
+
+        private Config(String tag, String setting, String intentAction, String permission,
+                String noun, int warningDialogTitle, int warningDialogSummary, int emptyText) {
+            this.tag = tag;
+            this.setting = setting;
+            this.intentAction = intentAction;
+            this.permission = permission;
+            this.noun = noun;
+            this.warningDialogTitle = warningDialogTitle;
+            this.warningDialogSummary = warningDialogSummary;
+            this.emptyText = emptyText;
+        }
+
+        public static class Builder{
+            private String mTag;
+            private String mSetting;
+            private String mIntentAction;
+            private String mPermission;
+            private String mNoun;
+            private int mWarningDialogTitle;
+            private int mWarningDialogSummary;
+            private int mEmptyText;
+
+            public Builder setTag(String tag) {
+                mTag = tag;
+                return this;
+            }
+
+            public Builder setSetting(String setting) {
+                mSetting = setting;
+                return this;
+            }
+
+            public Builder setIntentAction(String intentAction) {
+                mIntentAction = intentAction;
+                return this;
+            }
+
+            public Builder setPermission(String permission) {
+                mPermission = permission;
+                return this;
+            }
+
+            public Builder setNoun(String noun) {
+                mNoun = noun;
+                return this;
+            }
+
+            public Builder setWarningDialogTitle(int warningDialogTitle) {
+                mWarningDialogTitle = warningDialogTitle;
+                return this;
+            }
+
+            public Builder setWarningDialogSummary(int warningDialogSummary) {
+                mWarningDialogSummary = warningDialogSummary;
+                return this;
+            }
+
+            public Builder setEmptyText(int emptyText) {
+                mEmptyText = emptyText;
+                return this;
+            }
+
+            public Config build() {
+                return new Config(mTag, mSetting, mIntentAction, mPermission, mNoun,
+                        mWarningDialogTitle, mWarningDialogSummary, mEmptyText);
+            }
+        }
     }
+
 }
diff --git a/src/com/android/settings/utils/ServiceListing.java b/src/com/android/settings/utils/ServiceListing.java
deleted file mode 100644
index 6a5fa10..0000000
--- a/src/com/android/settings/utils/ServiceListing.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (C) 2015 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.utils;
-
-import android.app.ActivityManager;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Handler;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Slog;
-
-import com.android.settings.utils.ManagedServiceSettings.Config;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-
-public class ServiceListing {
-    private final ContentResolver mContentResolver;
-    private final Context mContext;
-    private final Config mConfig;
-    private final HashSet<ComponentName> mEnabledServices = new HashSet<ComponentName>();
-    private final List<ServiceInfo> mServices = new ArrayList<ServiceInfo>();
-    private final List<Callback> mCallbacks = new ArrayList<Callback>();
-
-    private boolean mListening;
-
-    public ServiceListing(Context context, Config config) {
-        mContext = context;
-        mConfig = config;
-        mContentResolver = context.getContentResolver();
-    }
-
-    public void addCallback(Callback callback) {
-        mCallbacks.add(callback);
-    }
-
-    public void removeCallback(Callback callback) {
-        mCallbacks.remove(callback);
-    }
-
-    public void setListening(boolean listening) {
-        if (mListening == listening) return;
-        mListening = listening;
-        if (mListening) {
-            // listen for package changes
-            IntentFilter filter = new IntentFilter();
-            filter.addAction(Intent.ACTION_PACKAGE_ADDED);
-            filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-            filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-            filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
-            filter.addDataScheme("package");
-            mContext.registerReceiver(mPackageReceiver, filter);
-            mContentResolver.registerContentObserver(Settings.Secure.getUriFor(mConfig.setting),
-                    false, mSettingsObserver);
-        } else {
-            mContext.unregisterReceiver(mPackageReceiver);
-            mContentResolver.unregisterContentObserver(mSettingsObserver);
-        }
-    }
-
-    public static int getEnabledServicesCount(Config config, Context context) {
-        final String flat = Settings.Secure.getString(context.getContentResolver(), config.setting);
-        if (flat == null || "".equals(flat)) return 0;
-        final String[] components = flat.split(":");
-        return components.length;
-    }
-
-    public static int getServicesCount(Config c, PackageManager pm) {
-        return getServices(c, null, pm);
-    }
-
-    protected static int getServices(Config c, List<ServiceInfo> list, PackageManager pm) {
-        int services = 0;
-        if (list != null) {
-            list.clear();
-        }
-        final int user = ActivityManager.getCurrentUser();
-
-        List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
-                new Intent(c.intentAction),
-                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
-                user);
-
-        for (int i = 0, count = installedServices.size(); i < count; i++) {
-            ResolveInfo resolveInfo = installedServices.get(i);
-            ServiceInfo info = resolveInfo.serviceInfo;
-
-            if (!c.permission.equals(info.permission)) {
-                Slog.w(c.tag, "Skipping " + c.noun + " service "
-                        + info.packageName + "/" + info.name
-                        + ": it does not require the permission "
-                        + c.permission);
-                continue;
-            }
-            if (list != null) {
-                list.add(info);
-            }
-            services++;
-        }
-        return services;
-    }
-
-    private void saveEnabledServices() {
-        StringBuilder sb = null;
-        for (ComponentName cn : mEnabledServices) {
-            if (sb == null) {
-                sb = new StringBuilder();
-            } else {
-                sb.append(':');
-            }
-            sb.append(cn.flattenToString());
-        }
-        Settings.Secure.putString(mContentResolver, mConfig.setting,
-                sb != null ? sb.toString() : "");
-    }
-
-    private void loadEnabledServices() {
-        mEnabledServices.clear();
-        final String flat = Settings.Secure.getString(mContentResolver, mConfig.setting);
-        if (flat != null && !"".equals(flat)) {
-            final String[] names = flat.split(":");
-            for (int i = 0; i < names.length; i++) {
-                final ComponentName cn = ComponentName.unflattenFromString(names[i]);
-                if (cn != null) {
-                    mEnabledServices.add(cn);
-                }
-            }
-        }
-    }
-
-    public List<ServiceInfo> reload() {
-        loadEnabledServices();
-        getServices(mConfig, mServices, mContext.getPackageManager());
-        for (Callback callback : mCallbacks) {
-            callback.onServicesReloaded(mServices);
-        }
-        return mServices;
-    }
-
-    public boolean isEnabled(ComponentName cn) {
-        return mEnabledServices.contains(cn);
-    }
-
-    public void setEnabled(ComponentName cn, boolean enabled) {
-        if (enabled) {
-            mEnabledServices.add(cn);
-        } else {
-            mEnabledServices.remove(cn);
-        }
-        saveEnabledServices();
-    }
-
-    private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            reload();
-        }
-    };
-
-    private final BroadcastReceiver mPackageReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            reload();
-        }
-    };
-
-    public interface Callback {
-        void onServicesReloaded(List<ServiceInfo> services);
-    }
-}
diff --git a/src/com/android/settings/wrapper/DevicePolicyManagerWrapper.java b/src/com/android/settings/wrapper/DevicePolicyManagerWrapper.java
index 2de964f..ccf6c53 100644
--- a/src/com/android/settings/wrapper/DevicePolicyManagerWrapper.java
+++ b/src/com/android/settings/wrapper/DevicePolicyManagerWrapper.java
@@ -223,7 +223,7 @@
      *
      * @see DevicePolicyManager#getMaximumTimeToLock(ComponentName, int)
      */
-    public long getMaximumTimeToLockForUserAndProfiles(@UserIdInt int userHandle) {
-        return mDpm.getMaximumTimeToLockForUserAndProfiles(userHandle);
+    public long getMaximumTimeToLock(@Nullable ComponentName admin, @UserIdInt int userHandle) {
+        return mDpm.getMaximumTimeToLock(admin, userHandle);
     }
 }
diff --git a/tests/robotests/src/com/android/settings/display/TimeoutPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/TimeoutPreferenceControllerTest.java
index 80a8ade..33bc4f8 100644
--- a/tests/robotests/src/com/android/settings/display/TimeoutPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/TimeoutPreferenceControllerTest.java
@@ -88,8 +88,7 @@
         final int profileUserId = UserHandle.myUserId();
         final long timeout = 10000;
         when(mUserManager.getProfiles(profileUserId)).thenReturn(Collections.emptyList());
-        ShadowDevicePolicyManagerWrapper
-                .setMaximumTimeToLockForUserAndProfiles(profileUserId, timeout);
+        ShadowDevicePolicyManagerWrapper.setMaximumTimeToLock(profileUserId, timeout);
 
         mController.updateState(mPreference);
         verify(mPreference).removeUnusableTimeouts(timeout, null);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
new file mode 100644
index 0000000..e4e8eef
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 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.fuelgauge.batterytip;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+
+import android.content.Context;
+
+import com.android.internal.os.BatteryStatsHelper;
+import com.android.settings.TestConfig;
+import com.android.settings.fuelgauge.batterytip.detectors.BatteryTipDetector;
+import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class BatteryTipLoaderTest {
+    @Mock
+    private BatteryStatsHelper mBatteryStatsHelper;
+    @Mock
+    private BatteryTipDetector mBatteryTipDetector;
+    @Mock
+    private BatteryTip mBatteryTip;
+    private Context mContext;
+    private BatteryTipLoader mBatteryTipLoader;
+    private List<BatteryTip> mBatteryTips;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = RuntimeEnvironment.application;
+        doReturn(mBatteryTip).when(mBatteryTipDetector).detect();
+        mBatteryTipLoader = new BatteryTipLoader(mContext, mBatteryStatsHelper);
+        mBatteryTips = new ArrayList<>();
+    }
+
+    @Test
+    public void testAddBatteryTipFromDetector_tipVisible_addAndUpdateCount() {
+        doReturn(true).when(mBatteryTip).isVisible();
+        mBatteryTipLoader.mVisibleTips = 0;
+
+        mBatteryTipLoader.addBatteryTipFromDetector(mBatteryTips, mBatteryTipDetector);
+
+        assertThat(mBatteryTips.contains(mBatteryTip)).isTrue();
+        assertThat(mBatteryTipLoader.mVisibleTips).isEqualTo(1);
+    }
+
+    @Test
+    public void testAddBatteryTipFromDetector_tipInvisible_doNotAddCount() {
+        doReturn(false).when(mBatteryTip).isVisible();
+        mBatteryTipLoader.mVisibleTips = 0;
+
+        mBatteryTipLoader.addBatteryTipFromDetector(mBatteryTips, mBatteryTipDetector);
+
+        assertThat(mBatteryTips.contains(mBatteryTip)).isTrue();
+        assertThat(mBatteryTipLoader.mVisibleTips).isEqualTo(0);
+    }
+
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java
new file mode 100644
index 0000000..1198b27
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2017 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.fuelgauge.batterytip;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class BatteryTipPolicyTest {
+    private static final String BATTERY_TIP_CONSTANTS_VALUE = "battery_tip_enabled=true"
+            + ",summary_enabled=false"
+            + ",battery_saver_tip_enabled=false"
+            + ",high_usage_enabled=true"
+            + ",high_usage_app_count=5"
+            + ",app_restriction_enabled=true"
+            + ",reduced_battery_enabled=true"
+            + ",reduced_battery_percent=30"
+            + ",low_battery_enabled=false"
+            + ",low_battery_hour=10";
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+    }
+
+    @Test
+    public void testInit_usesConfigValues() {
+        Settings.Global.putString(mContext.getContentResolver(),
+                Settings.Global.BATTERY_TIP_CONSTANTS, BATTERY_TIP_CONSTANTS_VALUE);
+
+        final BatteryTipPolicy batteryTipPolicy = new BatteryTipPolicy(mContext);
+
+        assertThat(batteryTipPolicy.batteryTipEnabled).isTrue();
+        assertThat(batteryTipPolicy.summaryEnabled).isFalse();
+        assertThat(batteryTipPolicy.batterySaverTipEnabled).isFalse();
+        assertThat(batteryTipPolicy.highUsageEnabled).isTrue();
+        assertThat(batteryTipPolicy.highUsageAppCount).isEqualTo(5);
+        assertThat(batteryTipPolicy.appRestrictionEnabled).isTrue();
+        assertThat(batteryTipPolicy.reducedBatteryEnabled).isTrue();
+        assertThat(batteryTipPolicy.reducedBatteryPercent).isEqualTo(30);
+        assertThat(batteryTipPolicy.lowBatteryEnabled).isFalse();
+        assertThat(batteryTipPolicy.lowBatteryHour).isEqualTo(10);
+    }
+
+    @Test
+    public void testInit_defaultValues() {
+        Settings.Global.putString(mContext.getContentResolver(),
+                Settings.Global.BATTERY_TIP_CONSTANTS, "");
+
+        final BatteryTipPolicy batteryTipPolicy = new BatteryTipPolicy(mContext);
+
+        assertThat(batteryTipPolicy.batteryTipEnabled).isTrue();
+        assertThat(batteryTipPolicy.summaryEnabled).isTrue();
+        assertThat(batteryTipPolicy.batterySaverTipEnabled).isTrue();
+        assertThat(batteryTipPolicy.highUsageEnabled).isTrue();
+        assertThat(batteryTipPolicy.highUsageAppCount).isEqualTo(3);
+        assertThat(batteryTipPolicy.appRestrictionEnabled).isTrue();
+        assertThat(batteryTipPolicy.reducedBatteryEnabled).isTrue();
+        assertThat(batteryTipPolicy.reducedBatteryPercent).isEqualTo(50);
+        assertThat(batteryTipPolicy.lowBatteryEnabled).isTrue();
+        assertThat(batteryTipPolicy.lowBatteryHour).isEqualTo(16);
+    }
+
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetectorTest.java
new file mode 100644
index 0000000..4866a6a
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetectorTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 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.fuelgauge.batterytip.detectors;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.text.format.DateUtils;
+
+import com.android.settings.TestConfig;
+import com.android.settings.fuelgauge.BatteryInfo;
+import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class LowBatteryDetectorTest {
+    private Context mContext;
+    @Mock
+    private BatteryInfo mBatteryInfo;
+    private BatteryTipPolicy mPolicy;
+    private LowBatteryDetector mLowBatteryDetector;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = RuntimeEnvironment.application;
+        mPolicy = spy(new BatteryTipPolicy(mContext));
+        mLowBatteryDetector = new LowBatteryDetector(mPolicy, mBatteryInfo);
+    }
+
+    @Test
+    public void testDetect_disabledByPolicy_tipInvisible() {
+        ReflectionHelpers.setField(mPolicy, "lowBatteryEnabled", false);
+
+        assertThat(mLowBatteryDetector.detect().isVisible()).isFalse();
+    }
+
+    @Test
+    public void testDetect_shortBatteryLife_tipVisible() {
+        mBatteryInfo.discharging = true;
+        mBatteryInfo.remainingTimeUs = 1 * DateUtils.MINUTE_IN_MILLIS;
+
+        assertThat(mLowBatteryDetector.detect().isVisible()).isTrue();
+    }
+
+    @Test
+    public void testDetect_longBatteryLife_tipInvisible() {
+        mBatteryInfo.discharging = true;
+        mBatteryInfo.remainingTimeUs = 1 * DateUtils.DAY_IN_MILLIS;
+
+        assertThat(mLowBatteryDetector.detect().isVisible()).isFalse();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/SummaryDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/SummaryDetectorTest.java
new file mode 100644
index 0000000..389a6c3
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/SummaryDetectorTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 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.fuelgauge.batterytip.detectors;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+
+import com.android.settings.TestConfig;
+import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class SummaryDetectorTest {
+    private Context mContext;
+    private BatteryTipPolicy mPolicy;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = RuntimeEnvironment.application;
+        mPolicy = spy(new BatteryTipPolicy(mContext));
+    }
+
+    @Test
+    public void testDetect_disabledByPolicy_tipInvisible() {
+        ReflectionHelpers.setField(mPolicy, "summaryEnabled", false);
+        SummaryDetector detector = new SummaryDetector(mPolicy, 0 /* visibleTips */);
+
+        assertThat(detector.detect().isVisible()).isFalse();
+    }
+
+    @Test
+    public void testDetect_noOtherTips_tipVisible() {
+        SummaryDetector detector = new SummaryDetector(mPolicy, 0 /* visibleTips */);
+
+        assertThat(detector.detect().isVisible()).isTrue();
+    }
+
+    @Test
+    public void testDetect_hasOtherTips_tipInVisible() {
+        SummaryDetector detector = new SummaryDetector(mPolicy, 1 /* visibleTips */);
+
+        assertThat(detector.detect().isVisible()).isFalse();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/security/screenlock/LockAfterTimeoutPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/security/screenlock/LockAfterTimeoutPreferenceControllerTest.java
index 4541e21..6afd25e 100644
--- a/tests/robotests/src/com/android/settings/security/screenlock/LockAfterTimeoutPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/security/screenlock/LockAfterTimeoutPreferenceControllerTest.java
@@ -129,8 +129,7 @@
         when(mPreference.getEntryValues()).thenReturn(new CharSequence[] {"10000"} );
 
         Settings.System.putInt(mContext.getContentResolver(), SCREEN_OFF_TIMEOUT, displayTimeout);
-        ShadowDevicePolicyManagerWrapper
-                .setMaximumTimeToLockForUserAndProfiles(userId, adminTimeout);
+        ShadowDevicePolicyManagerWrapper.setMaximumTimeToLock(userId, adminTimeout);
 
         mController.updateState((Preference) mPreference);
 
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDevicePolicyManagerWrapper.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDevicePolicyManagerWrapper.java
index 46a0038..5502b37 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDevicePolicyManagerWrapper.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDevicePolicyManagerWrapper.java
@@ -47,7 +47,7 @@
     }
 
     @Implementation
-    public long getMaximumTimeToLockForUserAndProfiles(@UserIdInt int userHandle) {
+    public long getMaximumTimeToLock(ComponentName admin, @UserIdInt int userHandle) {
         return profileTimeouts.getOrDefault(userHandle, 0L);
     }
 
@@ -59,8 +59,7 @@
         ShadowDevicePolicyManagerWrapper.deviceOwnerUserId = deviceOwnerUserId;
     }
 
-    public static void setMaximumTimeToLockForUserAndProfiles(
-            @UserIdInt int userHandle, Long timeout) {
+    public static void setMaximumTimeToLock(@UserIdInt int userHandle, Long timeout) {
         profileTimeouts.put(userHandle, timeout);
     }
 }