• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 package com.android.timezone.location.provider.core;
17 
18 import static com.android.timezone.location.provider.core.LogUtils.formatElapsedRealtimeMillis;
19 import static com.android.timezone.location.provider.core.OfflineLocationTimeZoneDelegate.LOCATION_LISTEN_MODE_ACTIVE;
20 import static com.android.timezone.location.provider.core.OfflineLocationTimeZoneDelegate.LOCATION_LISTEN_MODE_NA;
21 import static com.android.timezone.location.provider.core.OfflineLocationTimeZoneDelegate.LOCATION_LISTEN_MODE_PASSIVE;
22 
23 import android.os.SystemClock;
24 
25 import androidx.annotation.IntDef;
26 import androidx.annotation.NonNull;
27 import androidx.annotation.Nullable;
28 
29 import com.android.timezone.location.common.PiiLoggable;
30 import com.android.timezone.location.common.PiiLoggables;
31 import com.android.timezone.location.provider.core.OfflineLocationTimeZoneDelegate.ListenModeEnum;
32 
33 import java.util.Objects;
34 
35 /**
36  * Holds state associated with the {@link OfflineLocationTimeZoneDelegate}'s mode. This class exists
37  * to make it clear during debugging what transitions the {@link OfflineLocationTimeZoneDelegate}
38  * has gone through to arrive at its current state.
39  *
40  * <p>This class is not thread safe: {@link OfflineLocationTimeZoneDelegate} handles all
41  * synchronization.
42  *
43  * <p>See the docs for each {@code MODE_} constant for an explanation of each mode.
44  *
45  * <pre>
46  * The initial mode is {@link #MODE_STOPPED}.
47  *
48  * Valid transitions:
49  *
50  * {@link #MODE_STOPPED}
51  *   -> {@link #MODE_STARTED}
52  *       - when the LTZP first receives a "start" request it starts listening for the current
53  *         location.
54  * {@link #MODE_STARTED}
55  *   -> {@link #MODE_STOPPED}
56  *       - when the system server sends a "stopped" request it stops listening for the current
57  *         location.
58  *
59  * {All states}
60  *   -> {@link #MODE_FAILED} (terminal state)
61  *       - when there is a fatal error.
62  * {Most states}
63  *   -> {@link #MODE_DESTROYED} (terminal state)
64  *       - when the provider's service is destroyed, perhaps as part of the current user changing
65  * </pre>
66  */
67 class Mode implements PiiLoggable {
68 
69     @IntDef({ MODE_STOPPED, MODE_STARTED, MODE_FAILED, MODE_DESTROYED })
70     @interface ModeEnum {}
71 
72     /**
73      * An inactive state. The LTZP may not have received a request yet, or it has and the LTZP has
74      * been explicitly stopped.
75      */
76     @ModeEnum
77     static final int MODE_STOPPED = 1;
78 
79     /**
80      * The LTZP has been started by the system server, and is listening for the current location.
81      * The {@link #mListenMode} records the current type of listening.
82      */
83     @ModeEnum
84     static final int MODE_STARTED = 2;
85 
86     /**
87      * The LTZP's service has been destroyed.
88      */
89     @ModeEnum
90     static final int MODE_DESTROYED = 3;
91 
92     /**
93      * The LTZP encountered a failure it cannot recover from.
94      */
95     @ModeEnum
96     static final int MODE_FAILED = 4;
97 
98     /** The current mode. */
99     @ModeEnum
100     final int mModeEnum;
101 
102     /**
103      * The current location listen mode. Only used when mModeEnum == {@link #MODE_STARTED}.
104      */
105     final @ListenModeEnum int mListenMode;
106 
107     /**
108      * Debug information: The elapsed realtime recorded when the mode was created.
109      */
110     private final long mCreationElapsedRealtimeMillis;
111 
112     /**
113      * Debug information: Information about why the mode was entered.
114      */
115     @NonNull
116     private final PiiLoggable mEntryCause;
117 
118     /**
119      * Used when mModeEnum == {@link #MODE_STARTED}. The {@link Cancellable} that can be
120      * used to stop listening for the current location.
121      */
122     @Nullable
123     private final Cancellable mLocationListenerCancellable;
124 
Mode(@odeEnum int modeEnum, @NonNull PiiLoggable entryCause)125     Mode(@ModeEnum int modeEnum, @NonNull PiiLoggable entryCause) {
126         this(modeEnum, entryCause, LOCATION_LISTEN_MODE_NA, null);
127     }
128 
Mode(@odeEnum int modeEnum, @NonNull PiiLoggable entryCause, @ListenModeEnum int listenMode, @Nullable Cancellable listeningCancellable)129     Mode(@ModeEnum int modeEnum, @NonNull PiiLoggable entryCause, @ListenModeEnum int listenMode,
130             @Nullable Cancellable listeningCancellable) {
131         mModeEnum = validateModeEnum(modeEnum);
132         mListenMode = validateListenModeEnum(modeEnum, listenMode);
133         mEntryCause = Objects.requireNonNull(entryCause);
134         mLocationListenerCancellable = listeningCancellable;
135 
136         // Information useful for logging / debugging.
137         mCreationElapsedRealtimeMillis = SystemClock.elapsedRealtime();
138     }
139 
140     /** Returns the stopped mode which is the starting state for a provider. */
141     @NonNull
createStoppedMode()142     static Mode createStoppedMode() {
143         return new Mode(MODE_STOPPED, PiiLoggables.fromString("init") /* entryCause */);
144     }
145 
146     /**
147      * If this mode is associated with location listening, this invokes {@link
148      * Cancellable#cancel()}. If this mode is not associated with location listening, this is a
149      * no-op.
150      */
cancelLocationListening()151     void cancelLocationListening() {
152         if (mLocationListenerCancellable != null) {
153             mLocationListenerCancellable.cancel();
154         }
155     }
156 
157     @Override
toPiiString()158     public String toPiiString() {
159         String template = toStringTemplate();
160         return PiiLoggables.formatPiiString(template, mEntryCause);
161     }
162 
163     @Override
toString()164     public String toString() {
165         String template = toStringTemplate();
166         return String.format(template, mEntryCause);
167     }
168 
toStringTemplate()169     private String toStringTemplate() {
170         return "Mode{"
171                 + "mModeEnum=" + prettyPrintModeEnum(mModeEnum)
172                 + ", mListenMode=" + prettyPrintListenModeEnum(mListenMode)
173                 + ", mCreationElapsedRealtimeMillis="
174                 + formatElapsedRealtimeMillis(mCreationElapsedRealtimeMillis)
175                 + ", mEntryCause={%s}"
176                 + ", mLocationListenerCancellable=" + mLocationListenerCancellable
177                 + '}';
178     }
179 
180     /** Returns a string representation of the {@link ModeEnum} value provided. */
prettyPrintModeEnum(@odeEnum int modeEnum)181     static String prettyPrintModeEnum(@ModeEnum int modeEnum) {
182         switch (modeEnum) {
183             case MODE_STOPPED:
184                 return "MODE_STOPPED";
185             case MODE_STARTED:
186                 return "MODE_STARTED";
187             case MODE_DESTROYED:
188                 return "MODE_DESTROYED";
189             case MODE_FAILED:
190                 return "MODE_FAILED";
191             default:
192                 return modeEnum + " (Unknown)";
193         }
194     }
195 
196     /** Returns a string representation of the {@link ListenModeEnum} value provided. */
prettyPrintListenModeEnum(@istenModeEnum int listenMode)197     static String prettyPrintListenModeEnum(@ListenModeEnum int listenMode) {
198         switch (listenMode) {
199             case LOCATION_LISTEN_MODE_NA:
200                 return "LISTEN_MODE_NA";
201             case LOCATION_LISTEN_MODE_ACTIVE:
202                 return "LOCATION_LISTEN_MODE_ACTIVE";
203             case LOCATION_LISTEN_MODE_PASSIVE:
204                 return "LOCATION_LISTEN_MODE_PASSIVE";
205             default:
206                 return listenMode + " (Unknown)";
207         }
208     }
209 
validateModeEnum(@odeEnum int modeEnum)210     private static @ModeEnum int validateModeEnum(@ModeEnum int modeEnum) {
211         if (modeEnum < MODE_STOPPED || modeEnum > MODE_FAILED) {
212             throw new IllegalArgumentException("modeEnum=" + modeEnum);
213         }
214         return modeEnum;
215     }
216 
validateListenModeEnum( @odeEnum int modeEnum, @ListenModeEnum int listenMode)217     private static @ListenModeEnum int validateListenModeEnum(
218             @ModeEnum int modeEnum, @ListenModeEnum int listenMode) {
219         if (modeEnum == MODE_STARTED) {
220             if (listenMode != LOCATION_LISTEN_MODE_ACTIVE
221                     && listenMode != LOCATION_LISTEN_MODE_PASSIVE) {
222                 throw new IllegalArgumentException();
223             }
224         } else {
225             if (listenMode != LOCATION_LISTEN_MODE_NA) {
226                 throw new IllegalArgumentException();
227             }
228         }
229         return listenMode;
230     }
231 }
232