1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.fuelgauge.batterytip; 18 19 import android.app.StatsManager; 20 import android.app.job.JobInfo; 21 import android.app.job.JobParameters; 22 import android.app.job.JobScheduler; 23 import android.app.job.JobService; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.SharedPreferences; 27 import android.provider.Settings; 28 import android.support.annotation.VisibleForTesting; 29 import android.text.TextUtils; 30 import android.util.Base64; 31 import android.util.Log; 32 33 import com.android.settings.R; 34 import com.android.settingslib.utils.ThreadUtils; 35 36 import java.util.concurrent.TimeUnit; 37 38 /** A JobService check whether to update the anomaly config periodically */ 39 public class AnomalyConfigJobService extends JobService { 40 private static final String TAG = "AnomalyConfigJobService"; 41 42 public static final String PREF_DB = "anomaly_pref"; 43 public static final String KEY_ANOMALY_CONFIG_VERSION = "anomaly_config_version"; 44 private static final int DEFAULT_VERSION = 0; 45 46 @VisibleForTesting 47 static final long CONFIG_UPDATE_FREQUENCY_MS = TimeUnit.DAYS.toMillis(1); 48 49 public static void scheduleConfigUpdate(Context context) { 50 final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class); 51 52 final ComponentName component = new ComponentName(context, AnomalyConfigJobService.class); 53 final JobInfo.Builder jobBuilder = 54 new JobInfo.Builder(R.integer.job_anomaly_config_update, component) 55 .setPeriodic(CONFIG_UPDATE_FREQUENCY_MS) 56 .setRequiresDeviceIdle(true) 57 .setRequiresCharging(true) 58 .setPersisted(true); 59 final JobInfo pending = jobScheduler.getPendingJob(R.integer.job_anomaly_config_update); 60 61 // Don't schedule it if it already exists, to make sure it runs periodically even after 62 // reboot 63 if (pending == null && jobScheduler.schedule(jobBuilder.build()) 64 != JobScheduler.RESULT_SUCCESS) { 65 Log.i(TAG, "Anomaly config update job service schedule failed."); 66 } 67 } 68 69 @Override 70 public boolean onStartJob(JobParameters params) { 71 ThreadUtils.postOnBackgroundThread(() -> { 72 final StatsManager statsManager = getSystemService(StatsManager.class); 73 checkAnomalyConfig(statsManager); 74 try { 75 BatteryTipUtils.uploadAnomalyPendingIntent(this, statsManager); 76 } catch (StatsManager.StatsUnavailableException e) { 77 Log.w(TAG, "Failed to uploadAnomalyPendingIntent.", e); 78 } 79 jobFinished(params, false /* wantsReschedule */); 80 }); 81 82 return true; 83 } 84 85 @Override 86 public boolean onStopJob(JobParameters jobParameters) { 87 return false; 88 } 89 90 @VisibleForTesting 91 synchronized void checkAnomalyConfig(StatsManager statsManager) { 92 final SharedPreferences sharedPreferences = getSharedPreferences(PREF_DB, 93 Context.MODE_PRIVATE); 94 final int currentVersion = sharedPreferences.getInt(KEY_ANOMALY_CONFIG_VERSION, 95 DEFAULT_VERSION); 96 final int newVersion = Settings.Global.getInt(getContentResolver(), 97 Settings.Global.ANOMALY_CONFIG_VERSION, DEFAULT_VERSION); 98 final String rawConfig = Settings.Global.getString(getContentResolver(), 99 Settings.Global.ANOMALY_CONFIG); 100 Log.i(TAG, "CurrentVersion: " + currentVersion + " new version: " + newVersion); 101 102 if (newVersion > currentVersion) { 103 try { 104 statsManager.removeConfig(StatsManagerConfig.ANOMALY_CONFIG_KEY); 105 } catch (StatsManager.StatsUnavailableException e) { 106 Log.i(TAG, "When updating anomaly config, failed to first remove the old config " 107 + StatsManagerConfig.ANOMALY_CONFIG_KEY, e); 108 } 109 if (!TextUtils.isEmpty(rawConfig)) { 110 try { 111 final byte[] config = Base64.decode(rawConfig, Base64.DEFAULT); 112 statsManager.addConfig(StatsManagerConfig.ANOMALY_CONFIG_KEY, config); 113 Log.i(TAG, "Upload the anomaly config. configKey: " 114 + StatsManagerConfig.ANOMALY_CONFIG_KEY); 115 SharedPreferences.Editor editor = sharedPreferences.edit(); 116 editor.putInt(KEY_ANOMALY_CONFIG_VERSION, newVersion); 117 editor.commit(); 118 } catch (IllegalArgumentException e) { 119 Log.e(TAG, "Anomaly raw config is in wrong format", e); 120 } catch (StatsManager.StatsUnavailableException e) { 121 Log.i(TAG, "Upload of anomaly config failed for configKey " 122 + StatsManagerConfig.ANOMALY_CONFIG_KEY, e); 123 } 124 } 125 } 126 } 127 } 128