• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.timezone.location.provider.core;
18 
19 import androidx.annotation.NonNull;
20 import androidx.annotation.Nullable;
21 
22 import com.android.timezone.location.provider.core.Environment.LocationListeningResult;
23 import com.android.timezone.location.provider.core.OfflineLocationTimeZoneDelegate.ListenModeEnum;
24 
25 import java.time.Duration;
26 import java.util.Objects;
27 
28 /**
29  * A class that keeps track of the amount of active listening the provider is permitted to do and
30  * issues instructions for the {@link OfflineLocationTimeZoneDelegate} to follow.
31  *
32  * <p>This logic has been extracted from {@link OfflineLocationTimeZoneDelegate} to enable
33  * independent unit testing of the logic.
34  *
35  * <p>The goal of this class is to ensure that power usage stays below an upper bound. Passive
36  * location listening and periods in doze are treated as "free", but active location listening is
37  * treated as expensive and must be limited. The <em>actual</em> expense of active listening is
38  * dependent on factors that are hard to account for, so it is treated consistently as "worst case".
39  *
40  * <p>The accountant stores a "balance" of the amount of active listening it is allowed to do. The
41  * accountant ensures the balance stays between zero and an upper bound (cap). In order to accrue
42  * active listening time, time must be spent <em>not</em> active listening.
43  *
44  * <p>The balance is reduced by an amount when the {@link OfflineLocationTimeZoneDelegate} calls
45  * {@link #getNextListeningInstruction(long, LocationListeningResult)} and is given an instruction
46  * to perform active listening for that amount of time.
47  *
48  * <p>The active listening balance can be increased directly via {@link
49  * #depositActiveListeningAmount(Duration)} (e.g. to return previously allocated but unused
50  * active listening time), or indirectly via {@link #accrueActiveListeningBudget(Duration)}, which
51  * is called to record time elapsed while <em>not</em> active listening.
52  */
53 public interface LocationListeningAccountant {
54 
55     /**
56      * Deposit an amount of active listening. Used when budget previously allocated with
57      * {@link #getNextListeningInstruction(long, LocationListeningResult)} is not all used. The
58      * balance after this operation will not exceed the cap.
59      */
depositActiveListeningAmount(@onNull Duration amount)60     void depositActiveListeningAmount(@NonNull Duration amount);
61 
62     /**
63      * Accrue an amount of active listening by reporting the amount of time spent in passive
64      * listening mode. The balance after this operation will not exceed the cap.
65      */
accrueActiveListeningBudget(@onNull Duration timeInPassiveMode)66     void accrueActiveListeningBudget(@NonNull Duration timeInPassiveMode);
67 
68     /** An instruction for the {@link OfflineLocationTimeZoneDelegate} to follow. */
69     final class ListeningInstruction {
70 
71         /** The mode to listen in. */
72         @ListenModeEnum
73         public final int listenMode;
74 
75         /** How long to listen for. */
76         @NonNull
77         public final Duration duration;
78 
ListeningInstruction(@istenModeEnum int listenMode, @NonNull Duration duration)79         public ListeningInstruction(@ListenModeEnum int listenMode, @NonNull Duration duration) {
80             this.listenMode = listenMode;
81             this.duration = Objects.requireNonNull(duration);
82         }
83 
84         @Override
equals(Object o)85         public boolean equals(Object o) {
86             if (this == o) {
87                 return true;
88             }
89             if (o == null || getClass() != o.getClass()) {
90                 return false;
91             }
92             ListeningInstruction that = (ListeningInstruction) o;
93             return listenMode == that.listenMode
94                     && duration.equals(that.duration);
95         }
96 
97         @Override
hashCode()98         public int hashCode() {
99             return Objects.hash(listenMode, duration);
100         }
101 
102         @Override
toString()103         public String toString() {
104             return "ListeningInstruction{"
105                     + "listenMode=" + listenMode
106                     + ", duration=" + duration
107                     + '}';
108         }
109     }
110 
111     /** Returns the next listening instruction. */
112     @NonNull
getNextListeningInstruction(long elapsedRealtimeMillis, @Nullable LocationListeningResult lastLocationListeningResult)113     ListeningInstruction getNextListeningInstruction(long elapsedRealtimeMillis,
114             @Nullable LocationListeningResult lastLocationListeningResult);
115 
116     /**
117      * Withdraws the current active listening balance, leaving it at zero. Used when transferring
118      * budget from one accountant to another.
119      */
120     @NonNull
withdrawActiveListeningBalance()121     Duration withdrawActiveListeningBalance();
122 }
123