Serik Beketayev | 1a503e4 | 2018-06-15 12:04:50 -0700 | [diff] [blame] | 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.car.garagemode; |
| 18 | |
| 19 | import android.content.Context; |
| 20 | |
Eric Jeong | 4531c91 | 2021-05-04 10:54:35 -0700 | [diff] [blame] | 21 | import com.android.car.CarLog; |
Serik Beketayev | 1a503e4 | 2018-06-15 12:04:50 -0700 | [diff] [blame] | 22 | import com.android.car.R; |
| 23 | import com.android.internal.annotations.VisibleForTesting; |
Eric Jeong | 4531c91 | 2021-05-04 10:54:35 -0700 | [diff] [blame] | 24 | import com.android.server.utils.Slogf; |
Serik Beketayev | 1a503e4 | 2018-06-15 12:04:50 -0700 | [diff] [blame] | 25 | |
| 26 | import java.util.HashMap; |
| 27 | import java.util.LinkedList; |
| 28 | import java.util.Map; |
| 29 | |
| 30 | /** |
| 31 | * Default garage mode policy. |
| 32 | * |
| 33 | * The first wake up time is set to be 1am the next day. And it keeps waking up every day for a |
| 34 | * week. After that, wake up every 7 days for a month, and wake up every 30 days thereafter. |
| 35 | */ |
| 36 | class WakeupPolicy { |
Eric Jeong | 4531c91 | 2021-05-04 10:54:35 -0700 | [diff] [blame] | 37 | |
| 38 | private static final String TAG = CarLog.tagFor(GarageMode.class) + "_" |
| 39 | + WakeupPolicy.class.getSimpleName(); |
Serik Beketayev | c6ab8be | 2018-08-28 21:20:53 -0700 | [diff] [blame] | 40 | private static final Map<Character, Integer> TIME_UNITS_LOOKUP_SEC; |
Serik Beketayev | 1a503e4 | 2018-06-15 12:04:50 -0700 | [diff] [blame] | 41 | static { |
Serik Beketayev | c6ab8be | 2018-08-28 21:20:53 -0700 | [diff] [blame] | 42 | TIME_UNITS_LOOKUP_SEC = new HashMap<>(); |
| 43 | TIME_UNITS_LOOKUP_SEC.put('m', 60); |
| 44 | TIME_UNITS_LOOKUP_SEC.put('h', 3600); |
| 45 | TIME_UNITS_LOOKUP_SEC.put('d', 86400); |
Serik Beketayev | 1a503e4 | 2018-06-15 12:04:50 -0700 | [diff] [blame] | 46 | } |
| 47 | private LinkedList<WakeupInterval> mWakeupIntervals; |
| 48 | @VisibleForTesting protected int mIndex; |
| 49 | |
| 50 | WakeupPolicy(String[] policy) { |
| 51 | mWakeupIntervals = parsePolicy(policy); |
| 52 | mIndex = 0; |
| 53 | } |
| 54 | |
| 55 | /** |
| 56 | * Initializes Policy from config_garageModeCadence resource array. |
| 57 | * @param context to access resources |
| 58 | * @return Policy instance, created from values in resources |
| 59 | */ |
| 60 | public static WakeupPolicy initFromResources(Context context) { |
Eric Jeong | 4531c91 | 2021-05-04 10:54:35 -0700 | [diff] [blame] | 61 | Slogf.d(TAG, "Initiating WakupPolicy from resources ..."); |
Serik Beketayev | 1a503e4 | 2018-06-15 12:04:50 -0700 | [diff] [blame] | 62 | return new WakeupPolicy( |
| 63 | context.getResources().getStringArray(R.array.config_garageModeCadence)); |
| 64 | } |
| 65 | |
| 66 | /** |
Serik Beketayev | c6ab8be | 2018-08-28 21:20:53 -0700 | [diff] [blame] | 67 | * Returns the interval in seconds, which defines next wake up time. |
| 68 | * @return the interval in seconds |
Serik Beketayev | 1a503e4 | 2018-06-15 12:04:50 -0700 | [diff] [blame] | 69 | */ |
| 70 | public int getNextWakeUpInterval() { |
| 71 | if (mWakeupIntervals.size() == 0) { |
Eric Jeong | 4531c91 | 2021-05-04 10:54:35 -0700 | [diff] [blame] | 72 | Slogf.e(TAG, "No wake up policy configuration was loaded."); |
Serik Beketayev | 1a503e4 | 2018-06-15 12:04:50 -0700 | [diff] [blame] | 73 | return 0; |
| 74 | } |
| 75 | |
| 76 | int index = mIndex; |
| 77 | for (WakeupInterval wakeupTime : mWakeupIntervals) { |
| 78 | if (index <= wakeupTime.getNumAttempts()) { |
Serik Beketayev | c6ab8be | 2018-08-28 21:20:53 -0700 | [diff] [blame] | 79 | return wakeupTime.getWakeupInterval(); |
Serik Beketayev | 1a503e4 | 2018-06-15 12:04:50 -0700 | [diff] [blame] | 80 | } |
| 81 | index -= wakeupTime.getNumAttempts(); |
| 82 | } |
Eric Jeong | 4531c91 | 2021-05-04 10:54:35 -0700 | [diff] [blame] | 83 | Slogf.w(TAG, "No more garage mode wake ups scheduled; been sleeping too long."); |
Serik Beketayev | 1a503e4 | 2018-06-15 12:04:50 -0700 | [diff] [blame] | 84 | return 0; |
| 85 | } |
| 86 | |
| 87 | protected int getWakupIntervalsAmount() { |
| 88 | return mWakeupIntervals.size(); |
| 89 | } |
| 90 | |
| 91 | private LinkedList<WakeupInterval> parsePolicy(String[] policy) { |
| 92 | LinkedList<WakeupInterval> intervals = new LinkedList<>(); |
| 93 | if (policy == null || policy.length == 0) { |
Eric Jeong | 4531c91 | 2021-05-04 10:54:35 -0700 | [diff] [blame] | 94 | Slogf.e(TAG, "Trying to parse empty policies!"); |
Serik Beketayev | 1a503e4 | 2018-06-15 12:04:50 -0700 | [diff] [blame] | 95 | return intervals; |
| 96 | } |
| 97 | |
| 98 | for (String rule : policy) { |
| 99 | WakeupInterval interval = parseRule(rule); |
| 100 | if (interval == null) { |
Eric Jeong | 4531c91 | 2021-05-04 10:54:35 -0700 | [diff] [blame] | 101 | Slogf.e(TAG, "Invalid Policy! This rule has bad format: %s", rule); |
Serik Beketayev | 1a503e4 | 2018-06-15 12:04:50 -0700 | [diff] [blame] | 102 | return new LinkedList<>(); |
| 103 | } |
| 104 | intervals.add(interval); |
| 105 | } |
| 106 | return intervals; |
| 107 | } |
| 108 | |
| 109 | private WakeupInterval parseRule(String rule) { |
| 110 | String[] str = rule.split(","); |
| 111 | |
| 112 | if (str.length != 2) { |
Eric Jeong | 4531c91 | 2021-05-04 10:54:35 -0700 | [diff] [blame] | 113 | Slogf.e(TAG, "Policy has bad format: %s", rule); |
Serik Beketayev | 1a503e4 | 2018-06-15 12:04:50 -0700 | [diff] [blame] | 114 | return null; |
| 115 | } |
| 116 | |
| 117 | String intervalStr = str[0]; |
| 118 | String timesStr = str[1]; |
| 119 | |
| 120 | if (intervalStr.isEmpty() || timesStr.isEmpty()) { |
Eric Jeong | 4531c91 | 2021-05-04 10:54:35 -0700 | [diff] [blame] | 121 | Slogf.e(TAG, "One of the values is empty. Please check format: %s", rule); |
Serik Beketayev | 1a503e4 | 2018-06-15 12:04:50 -0700 | [diff] [blame] | 122 | return null; |
| 123 | } |
| 124 | |
| 125 | char unit = intervalStr.charAt(intervalStr.length() - 1); |
| 126 | |
| 127 | // Removing last letter extension from string |
| 128 | intervalStr = intervalStr.substring(0, intervalStr.length() - 1); |
| 129 | |
| 130 | int interval, times; |
| 131 | try { |
| 132 | interval = Integer.parseInt(intervalStr); |
| 133 | times = Integer.parseInt(timesStr); |
| 134 | } catch (NumberFormatException ex) { |
Eric Jeong | 4531c91 | 2021-05-04 10:54:35 -0700 | [diff] [blame] | 135 | Slogf.d(TAG, "Invalid input Rule for interval %s", rule); |
Serik Beketayev | 1a503e4 | 2018-06-15 12:04:50 -0700 | [diff] [blame] | 136 | return null; |
| 137 | } |
| 138 | |
Serik Beketayev | c6ab8be | 2018-08-28 21:20:53 -0700 | [diff] [blame] | 139 | if (!TIME_UNITS_LOOKUP_SEC.containsKey(unit)) { |
Eric Jeong | 4531c91 | 2021-05-04 10:54:35 -0700 | [diff] [blame] | 140 | Slogf.e(TAG, "Time units map does not contain extension %c", unit); |
Serik Beketayev | 1a503e4 | 2018-06-15 12:04:50 -0700 | [diff] [blame] | 141 | return null; |
| 142 | } |
| 143 | |
| 144 | if (interval <= 0) { |
Eric Jeong | 4531c91 | 2021-05-04 10:54:35 -0700 | [diff] [blame] | 145 | Slogf.e(TAG, "Wake up policy time(%d) must be > 0!", interval); |
Serik Beketayev | 1a503e4 | 2018-06-15 12:04:50 -0700 | [diff] [blame] | 146 | return null; |
| 147 | } |
| 148 | |
| 149 | if (times <= 0) { |
Eric Jeong | 4531c91 | 2021-05-04 10:54:35 -0700 | [diff] [blame] | 150 | Slogf.e(TAG, "Wake up attempts(%d) in policy must be > 0!", times); |
Serik Beketayev | 1a503e4 | 2018-06-15 12:04:50 -0700 | [diff] [blame] | 151 | return null; |
| 152 | } |
| 153 | |
Serik Beketayev | c6ab8be | 2018-08-28 21:20:53 -0700 | [diff] [blame] | 154 | interval *= TIME_UNITS_LOOKUP_SEC.get(unit); |
Serik Beketayev | 1a503e4 | 2018-06-15 12:04:50 -0700 | [diff] [blame] | 155 | |
| 156 | return new WakeupInterval(interval, times); |
| 157 | } |
| 158 | |
| 159 | public void incrementCounter() { |
| 160 | mIndex++; |
| 161 | } |
| 162 | |
| 163 | public void resetCounter() { |
| 164 | mIndex = 0; |
| 165 | } |
| 166 | |
| 167 | /** |
| 168 | * Defines wake up interval which then will be used by |
Serik Beketayev | c6ab8be | 2018-08-28 21:20:53 -0700 | [diff] [blame] | 169 | * {@link com.android.car.garagemode.GarageModeService} to schedule next wake up time in |
| 170 | * {@link android.car.hardware.power.CarPowerManager} |
Serik Beketayev | 1a503e4 | 2018-06-15 12:04:50 -0700 | [diff] [blame] | 171 | */ |
| 172 | private class WakeupInterval { |
Serik Beketayev | c6ab8be | 2018-08-28 21:20:53 -0700 | [diff] [blame] | 173 | private int mWakeupInterval; |
Serik Beketayev | 1a503e4 | 2018-06-15 12:04:50 -0700 | [diff] [blame] | 174 | private int mNumAttempts; |
| 175 | |
| 176 | WakeupInterval(int wakeupTime, int numAttempts) { |
Serik Beketayev | c6ab8be | 2018-08-28 21:20:53 -0700 | [diff] [blame] | 177 | mWakeupInterval = wakeupTime; |
Serik Beketayev | 1a503e4 | 2018-06-15 12:04:50 -0700 | [diff] [blame] | 178 | mNumAttempts = numAttempts; |
| 179 | } |
| 180 | |
| 181 | /** |
Serik Beketayev | c6ab8be | 2018-08-28 21:20:53 -0700 | [diff] [blame] | 182 | * Returns interval between now and next wakeup. |
Serik Beketayev | 1a503e4 | 2018-06-15 12:04:50 -0700 | [diff] [blame] | 183 | * @return interval in seconds |
| 184 | */ |
Serik Beketayev | c6ab8be | 2018-08-28 21:20:53 -0700 | [diff] [blame] | 185 | public int getWakeupInterval() { |
| 186 | return mWakeupInterval; |
Serik Beketayev | 1a503e4 | 2018-06-15 12:04:50 -0700 | [diff] [blame] | 187 | } |
| 188 | |
| 189 | /** |
| 190 | * Returns amount of attempts to wake up with mWakeupInterval |
| 191 | * @return amount of attempts |
| 192 | */ |
| 193 | public int getNumAttempts() { |
| 194 | return mNumAttempts; |
| 195 | } |
| 196 | } |
| 197 | |
| 198 | } |