• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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.server.deviceconfig;
18 
19 import static com.android.server.deviceconfig.Flags.enableCustomRebootTimeConfigurations;
20 
21 import android.content.Context;
22 import android.content.pm.PackageManager.NameNotFoundException;
23 import android.content.res.Resources;
24 import android.util.Log;
25 import android.util.Pair;
26 
27 import com.android.internal.annotations.VisibleForTesting;
28 import com.android.server.deviceconfig.resources.R;
29 
30 import java.util.Optional;
31 
32 /**
33  * Contains the timing configuration for unattended reboot.
34  *
35  * @hide
36  */
37 public class RebootTimingConfiguration {
38     private static final String TAG = "RebootTimingConfiguration";
39 
40     private static final String RESOURCES_PACKAGE =
41         "com.android.server.deviceconfig.resources";
42 
43     /**
44      * Special value that can be set for both the window start and end hour configs to disable
45      * reboot time window constraint.
46      */
47     private static final int ALLOW_ALL_HOURS = -1;
48 
49     private static final Pair<Integer, Integer> DEFAULT_REBOOT_WINDOW_HOURS = Pair.create(3, 5);
50 
51     private static final int DEFAULT_REBOOT_FREQUENCY_DAYS = 2;
52 
53     private final Optional<Pair<Integer, Integer>> mRebootWindowStartEndHour;
54     private final int mRebootFrequencyDays;
55 
RebootTimingConfiguration(Context context)56     public RebootTimingConfiguration(Context context) {
57         if (enableCustomRebootTimeConfigurations()) {
58             Optional<Context> resourcesContext = getResourcesContext(context);
59             if (resourcesContext.isPresent()) {
60                 Resources res = resourcesContext.get().getResources();
61                 mRebootWindowStartEndHour = getRebootWindowStartEndHour(res);
62                 mRebootFrequencyDays = getRebootFrequencyDays(res);
63                 Log.d(TAG,
64                         "reboot start/end hour: " + mRebootWindowStartEndHour
65                         + "; frequency-days: " + mRebootFrequencyDays);
66                 return;
67             } else {
68                 Log.d(TAG, "Unable to get resources context");
69             }
70         }
71 
72         mRebootWindowStartEndHour = Optional.of(DEFAULT_REBOOT_WINDOW_HOURS);
73         mRebootFrequencyDays = DEFAULT_REBOOT_FREQUENCY_DAYS;
74     }
75 
76     @VisibleForTesting
RebootTimingConfiguration( int rebootWindowStartHour, int rebootWindowEndHour, int rebootFrequencyDays)77     RebootTimingConfiguration(
78             int rebootWindowStartHour, int rebootWindowEndHour, int rebootFrequencyDays) {
79         mRebootWindowStartEndHour =
80                 getRebootWindowStartEndHour(rebootWindowStartHour, rebootWindowEndHour);
81         assert(isDayValid(rebootFrequencyDays));
82         mRebootFrequencyDays = rebootFrequencyDays;
83     }
84 
85     /**
86      * Returns a {@link Pair} of integers, where the first element represents the reboot window
87      * start hour (inclusive), and the second element represents the reboot window end hour
88      * (exclusive). If the start hour is bigger than the end hour, it means that the end hour falls
89      * on the next day.
90      *
91      * <p>Returns an empty {@link Optional} if there is no reboot window constraint (i.e. if all
92      * hours are valid).
93      *
94      * <p>Use {@link #isHourWithinRebootHourWindow(int)} to validate if an hour falls within the
95      * window defined in this configuration.
96      */
getRebootWindowStartEndHour()97     public Optional<Pair<Integer, Integer>> getRebootWindowStartEndHour() {
98         return mRebootWindowStartEndHour;
99     }
100 
101     /** Returns the reboot frequency in days. */
getRebootFrequencyDays()102     public int getRebootFrequencyDays() {
103         return mRebootFrequencyDays;
104     }
105 
106     /**
107      * Returns {@code true} if the provided {@code hour} falls within the reboot window defined in
108      * this configuration.
109      */
isHourWithinRebootHourWindow(int hour)110     public boolean isHourWithinRebootHourWindow(int hour) {
111         if (!isHourValid(hour)) {
112             return false;
113         }
114         if (mRebootWindowStartEndHour.isEmpty()) {
115             return true;
116         }
117         Pair<Integer, Integer> rebootWindowStartEndHour = mRebootWindowStartEndHour.get();
118         if (rebootWindowStartEndHour.first < rebootWindowStartEndHour.second) {
119             // Window lies in a single day.
120             return hour >= rebootWindowStartEndHour.first && hour < rebootWindowStartEndHour.second;
121         }
122         // Window ends on the next day.
123         return hour >= rebootWindowStartEndHour.first || hour < rebootWindowStartEndHour.second;
124     }
125 
getRebootWindowStartEndHour(Resources res)126     private static Optional<Pair<Integer, Integer>> getRebootWindowStartEndHour(Resources res) {
127         return getRebootWindowStartEndHour(
128                 res.getInteger(R.integer.config_unattendedRebootStartHour),
129                 res.getInteger(R.integer.config_unattendedRebootEndHour));
130     }
131 
getRebootWindowStartEndHour( int configStartHour, int configEndHour)132     private static Optional<Pair<Integer, Integer>> getRebootWindowStartEndHour(
133             int configStartHour, int configEndHour) {
134         if (configStartHour == ALLOW_ALL_HOURS && configEndHour == ALLOW_ALL_HOURS) {
135             return Optional.empty();
136         }
137         if (!isHourValid(configStartHour)
138                 || !isHourValid(configEndHour)
139                 || configStartHour == configEndHour) {
140             return Optional.of(DEFAULT_REBOOT_WINDOW_HOURS);
141         }
142         return Optional.of(Pair.create(configStartHour, configEndHour));
143     }
144 
getRebootFrequencyDays(Resources res)145     private static int getRebootFrequencyDays(Resources res) {
146         int frequencyDays = res.getInteger(R.integer.config_unattendedRebootFrequencyDays);
147         if (!isDayValid(frequencyDays)) {
148             frequencyDays = DEFAULT_REBOOT_FREQUENCY_DAYS;
149         }
150         return frequencyDays;
151     }
152 
isHourValid(int hour)153     private static boolean isHourValid(int hour) {
154         return hour >= 0 && hour <= 23;
155     }
156 
isHourWindowValid(int startHour, int endHour)157     private static boolean isHourWindowValid(int startHour, int endHour) {
158         return isHourValid(startHour) && isHourValid(endHour) && startHour != endHour;
159     }
160 
isDayValid(int day)161     private static boolean isDayValid(int day) {
162         return day > 0;
163     }
164 
getResourcesContext(Context context)165     private static Optional<Context> getResourcesContext(Context context) {
166         ServiceResourcesHelper resourcesHelper = ServiceResourcesHelper.get(context);
167         Optional<String> resourcesPackageName = resourcesHelper.getResourcesPackageName();
168         if (resourcesPackageName.isPresent()) {
169             try {
170                 return Optional.ofNullable(
171                         context.createPackageContext(resourcesPackageName.get(), 0));
172             } catch (NameNotFoundException e) {
173                 Log.e(TAG, "Error in creating resources package context.", e);
174             }
175         }
176         return Optional.empty();
177     }
178 }