Merge changes Ib497aca6,Ic6def54a into pi-dev

* changes:
  Make anomaly jobs persistent.
  Update job ids in Settings
diff --git a/res/values/ids.xml b/res/values/ids.xml
index 57031e1..66af163 100644
--- a/res/values/ids.xml
+++ b/res/values/ids.xml
@@ -18,9 +18,6 @@
 -->
 <resources>
     <item type="id" name="preference_highlighted" />
-    <item type="id" name="job_anomaly_clean_up" />
-    <item type="id" name="job_anomaly_config_update"/>
-    <item type="id" name="job_anomaly_detection" />
 
     <item type="id" name="lock_none" />
     <item type="id" name="lock_pin" />
diff --git a/res/values/integers.xml b/res/values/integers.xml
new file mode 100644
index 0000000..ac9a973
--- /dev/null
+++ b/res/values/integers.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2018 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.
+  -->
+<resources>
+    <!-- Reserve all the job ids in settings -->
+    <integer name="job_anomaly_clean_up">100</integer>
+    <integer name="job_anomaly_config_update">101</integer>
+    <integer name="job_anomaly_detection">102</integer>
+</resources>
\ No newline at end of file
diff --git a/src/com/android/settings/fuelgauge/batterytip/AnomalyCleanupJobService.java b/src/com/android/settings/fuelgauge/batterytip/AnomalyCleanupJobService.java
index 46744f7..9e57433 100644
--- a/src/com/android/settings/fuelgauge/batterytip/AnomalyCleanupJobService.java
+++ b/src/com/android/settings/fuelgauge/batterytip/AnomalyCleanupJobService.java
@@ -42,12 +42,17 @@
 
         final ComponentName component = new ComponentName(context, AnomalyCleanupJobService.class);
         final JobInfo.Builder jobBuilder =
-                new JobInfo.Builder(R.id.job_anomaly_clean_up, component)
+                new JobInfo.Builder(R.integer.job_anomaly_clean_up, component)
                         .setPeriodic(CLEAN_UP_FREQUENCY_MS)
                         .setRequiresDeviceIdle(true)
-                        .setRequiresCharging(true);
+                        .setRequiresCharging(true)
+                        .setPersisted(true);
+        final JobInfo pending = jobScheduler.getPendingJob(R.integer.job_anomaly_clean_up);
 
-        if (jobScheduler.schedule(jobBuilder.build()) != JobScheduler.RESULT_SUCCESS) {
+        // Don't schedule it if it already exists, to make sure it runs periodically even after
+        // reboot
+        if (pending == null && jobScheduler.schedule(jobBuilder.build())
+                != JobScheduler.RESULT_SUCCESS) {
             Log.i(TAG, "Anomaly clean up job service schedule failed.");
         }
     }
diff --git a/src/com/android/settings/fuelgauge/batterytip/AnomalyConfigJobService.java b/src/com/android/settings/fuelgauge/batterytip/AnomalyConfigJobService.java
index 1a65088..98eb23e 100644
--- a/src/com/android/settings/fuelgauge/batterytip/AnomalyConfigJobService.java
+++ b/src/com/android/settings/fuelgauge/batterytip/AnomalyConfigJobService.java
@@ -52,12 +52,17 @@
 
         final ComponentName component = new ComponentName(context, AnomalyConfigJobService.class);
         final JobInfo.Builder jobBuilder =
-                new JobInfo.Builder(R.id.job_anomaly_config_update, component)
+                new JobInfo.Builder(R.integer.job_anomaly_config_update, component)
                         .setPeriodic(CONFIG_UPDATE_FREQUENCY_MS)
                         .setRequiresDeviceIdle(true)
-                        .setRequiresCharging(true);
+                        .setRequiresCharging(true)
+                        .setPersisted(true);
+        final JobInfo pending = jobScheduler.getPendingJob(R.integer.job_anomaly_config_update);
 
-        if (jobScheduler.schedule(jobBuilder.build()) != JobScheduler.RESULT_SUCCESS) {
+        // Don't schedule it if it already exists, to make sure it runs periodically even after
+        // reboot
+        if (pending == null && jobScheduler.schedule(jobBuilder.build())
+                != JobScheduler.RESULT_SUCCESS) {
             Log.i(TAG, "Anomaly config update job service schedule failed.");
         }
     }
diff --git a/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java b/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java
index a12d31e..5255578 100644
--- a/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java
+++ b/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java
@@ -70,7 +70,7 @@
         final ComponentName component = new ComponentName(context,
                 AnomalyDetectionJobService.class);
         final JobInfo.Builder jobBuilder =
-                new JobInfo.Builder(R.id.job_anomaly_detection, component)
+                new JobInfo.Builder(R.integer.job_anomaly_detection, component)
                         .setOverrideDeadline(MAX_DELAY_MS);
 
         if (jobScheduler.enqueue(jobBuilder.build(), new JobWorkItem(intent))
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyCleanupJobServiceTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyCleanupJobServiceTest.java
index 0e7fbc5..a39276d 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyCleanupJobServiceTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyCleanupJobServiceTest.java
@@ -18,10 +18,16 @@
 
 import static com.google.common.truth.Truth.assertThat;
 import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 import static org.robolectric.RuntimeEnvironment.application;
 
 import android.app.job.JobInfo;
 import android.app.job.JobScheduler;
+import android.content.Context;
 
 import com.android.settings.R;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
@@ -30,6 +36,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
 import org.robolectric.Shadows;
 import org.robolectric.shadows.ShadowJobScheduler;
 
@@ -38,23 +45,39 @@
 
 @RunWith(SettingsRobolectricTestRunner.class)
 public class AnomalyCleanupJobServiceTest {
+    private Context mContext;
+    private JobScheduler mJobScheduler;
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+
+        mContext = spy(RuntimeEnvironment.application);
+        mJobScheduler = spy(mContext.getSystemService(JobScheduler.class));
+        when(mContext.getSystemService(JobScheduler.class)).thenReturn(mJobScheduler);
     }
 
     @Test
     public void testScheduleCleanUp() {
-        AnomalyCleanupJobService.scheduleCleanUp(application);
+        AnomalyCleanupJobService.scheduleCleanUp(mContext);
 
         ShadowJobScheduler shadowJobScheduler =
-            Shadows.shadowOf(application.getSystemService(JobScheduler.class));
+            Shadows.shadowOf(mContext.getSystemService(JobScheduler.class));
         List<JobInfo> pendingJobs = shadowJobScheduler.getAllPendingJobs();
         assertEquals(1, pendingJobs.size());
         JobInfo pendingJob = pendingJobs.get(0);
-        assertThat(pendingJob.getId()).isEqualTo(R.id.job_anomaly_clean_up);
+        assertThat(pendingJob.getId()).isEqualTo(R.integer.job_anomaly_clean_up);
         assertThat(pendingJob.getIntervalMillis()).isEqualTo(TimeUnit.DAYS.toMillis(1));
         assertThat(pendingJob.isRequireDeviceIdle()).isTrue();
         assertThat(pendingJob.isRequireCharging()).isTrue();
+        assertThat(pendingJob.isPersisted()).isTrue();
+    }
+
+    @Test
+    public void testScheduleCleanUp_invokeTwice_onlyScheduleOnce() {
+        AnomalyCleanupJobService.scheduleCleanUp(mContext);
+        AnomalyCleanupJobService.scheduleCleanUp(mContext);
+
+        verify(mJobScheduler, times(1)).schedule(any());
     }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyConfigJobServiceTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyConfigJobServiceTest.java
index e1b85aa..90af7b1 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyConfigJobServiceTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyConfigJobServiceTest.java
@@ -25,7 +25,9 @@
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 import static org.robolectric.RuntimeEnvironment.application;
 
 import android.app.StatsManager;
@@ -43,6 +45,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
 import org.robolectric.Shadows;
 import org.robolectric.shadows.ShadowJobScheduler;
 
@@ -57,12 +60,18 @@
     @Mock
     private StatsManager mStatsManager;
 
+    private Context mContext;
+    private JobScheduler mJobScheduler;
     private AnomalyConfigJobService mJobService;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
+        mContext = spy(RuntimeEnvironment.application);
+        mJobScheduler = spy(mContext.getSystemService(JobScheduler.class));
+        when(mContext.getSystemService(JobScheduler.class)).thenReturn(mJobScheduler);
+
         mJobService = spy(new AnomalyConfigJobService());
         doReturn(application.getSharedPreferences(AnomalyConfigJobService.PREF_DB,
                 Context.MODE_PRIVATE)).when(mJobService).getSharedPreferences(anyString(),
@@ -71,18 +80,27 @@
     }
 
     @Test
-    public void testScheduleCleanUp() {
-        AnomalyConfigJobService.scheduleConfigUpdate(application);
+    public void testScheduleConfigUpdate() {
+        AnomalyConfigJobService.scheduleConfigUpdate(mContext);
 
         ShadowJobScheduler shadowJobScheduler =
-                Shadows.shadowOf(application.getSystemService(JobScheduler.class));
+                Shadows.shadowOf(mContext.getSystemService(JobScheduler.class));
         List<JobInfo> pendingJobs = shadowJobScheduler.getAllPendingJobs();
         assertEquals(1, pendingJobs.size());
         JobInfo pendingJob = pendingJobs.get(0);
-        assertThat(pendingJob.getId()).isEqualTo(R.id.job_anomaly_config_update);
+        assertThat(pendingJob.getId()).isEqualTo(R.integer.job_anomaly_config_update);
         assertThat(pendingJob.getIntervalMillis()).isEqualTo(TimeUnit.DAYS.toMillis(1));
         assertThat(pendingJob.isRequireDeviceIdle()).isTrue();
         assertThat(pendingJob.isRequireCharging()).isTrue();
+        assertThat(pendingJob.isPersisted()).isTrue();
+    }
+
+    @Test
+    public void testScheduleConfigUpdate_invokeTwice_onlyScheduleOnce() {
+        AnomalyConfigJobService.scheduleConfigUpdate(mContext);
+        AnomalyConfigJobService.scheduleConfigUpdate(mContext);
+
+        verify(mJobScheduler, times(1)).schedule(any());
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobServiceTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobServiceTest.java
index efc20e9..0894b66 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobServiceTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobServiceTest.java
@@ -113,7 +113,7 @@
         assertThat(pendingJobs).hasSize(1);
 
         JobInfo pendingJob = pendingJobs.get(0);
-        assertThat(pendingJob.getId()).isEqualTo(R.id.job_anomaly_detection);
+        assertThat(pendingJob.getId()).isEqualTo(R.integer.job_anomaly_detection);
         assertThat(pendingJob.getMaxExecutionDelayMillis())
                 .isEqualTo(TimeUnit.MINUTES.toMillis(30));
     }