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.server.devicestate;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.hardware.devicestate.DeviceStateRequest;
22 import android.os.IBinder;
23 import android.util.Slog;
24 
25 import java.io.PrintWriter;
26 import java.lang.annotation.Retention;
27 import java.lang.annotation.RetentionPolicy;
28 
29 /**
30  * Manages the lifecycle of override requests.
31  * <p>
32  * New requests are added with {@link #addRequest(OverrideRequest)} and are kept active until
33  * either:
34  * <ul>
35  *     <li>A new request is added with {@link #addRequest(OverrideRequest)}, in which case the
36  *     request will become suspended.</li>
37  *     <li>The request is cancelled with {@link #cancelRequest} or as a side effect
38  *     of other methods calls, such as {@link #handleProcessDied(int)}.</li>
39  * </ul>
40  */
41 final class OverrideRequestController {
42     private static final String TAG = "OverrideRequestController";
43 
44     static final int STATUS_UNKNOWN = 0;
45     /**
46      * The request is the top-most request.
47      */
48     static final int STATUS_ACTIVE = 1;
49     /**
50      * The request is not longer valid.
51      */
52     static final int STATUS_CANCELED = 2;
53 
54     @IntDef(prefix = {"STATUS_"}, value = {
55             STATUS_UNKNOWN,
56             STATUS_ACTIVE,
57             STATUS_CANCELED
58     })
59     @Retention(RetentionPolicy.SOURCE)
60     @interface RequestStatus {}
61 
62     /**
63      * A flag indicating that the status change was triggered by thermal critical status.
64      */
65     static final int FLAG_THERMAL_CRITICAL = 1 << 0;
66 
67     /**
68      * A flag indicating that the status change was triggered by power save mode.
69      */
70     static final int FLAG_POWER_SAVE_ENABLED = 1 << 1;
71 
72     @IntDef(flag = true, prefix = {"FLAG_"}, value = {
73             FLAG_THERMAL_CRITICAL,
74             FLAG_POWER_SAVE_ENABLED
75     })
76     @Retention(RetentionPolicy.SOURCE)
77     @interface StatusChangedFlag {}
78 
statusToString(@equestStatus int status)79     static String statusToString(@RequestStatus int status) {
80         switch (status) {
81             case STATUS_ACTIVE:
82                 return "ACTIVE";
83             case STATUS_CANCELED:
84                 return "CANCELED";
85             case STATUS_UNKNOWN:
86                 return "UNKNOWN";
87         }
88         throw new IllegalArgumentException("Unknown status: " + status);
89     }
90 
91     private final StatusChangeListener mListener;
92 
93     // Handle to the current override request, null if none.
94     private OverrideRequest mRequest;
95     // Handle to the current base state override request, null if none.
96     private OverrideRequest mBaseStateRequest;
97 
98     private boolean mStickyRequestsAllowed;
99     // The current request has outlived their process.
100     private boolean mStickyRequest;
101 
OverrideRequestController(@onNull StatusChangeListener listener)102     OverrideRequestController(@NonNull StatusChangeListener listener) {
103         mListener = listener;
104     }
105 
106     /**
107      * Sets sticky requests as either allowed or disallowed. When sticky requests are allowed a call
108      * to {@link #handleProcessDied(int)} will not result in the request being cancelled
109      * immediately. Instead, the request will be marked sticky and must be cancelled with a call
110      * to {@link #cancelStickyRequest()}.
111      */
setStickyRequestsAllowed(boolean stickyRequestsAllowed)112     void setStickyRequestsAllowed(boolean stickyRequestsAllowed) {
113         mStickyRequestsAllowed = stickyRequestsAllowed;
114         if (!mStickyRequestsAllowed) {
115             cancelStickyRequest();
116         }
117     }
118 
119     /**
120      * Sets the new request as active and cancels the previous override request, notifies the
121      * listener of all changes to request status as a result of this operation.
122      */
addRequest(@onNull OverrideRequest request)123     void addRequest(@NonNull OverrideRequest request) {
124         OverrideRequest previousRequest = mRequest;
125         mRequest = request;
126         mListener.onStatusChanged(request, STATUS_ACTIVE, 0 /* flags */);
127 
128         if (previousRequest != null) {
129             cancelRequestLocked(previousRequest);
130         }
131     }
132 
addBaseStateRequest(@onNull OverrideRequest request)133     void addBaseStateRequest(@NonNull OverrideRequest request) {
134         OverrideRequest previousRequest = mBaseStateRequest;
135         mBaseStateRequest = request;
136         mListener.onStatusChanged(request, STATUS_ACTIVE, 0 /* flags */);
137 
138         if (previousRequest != null) {
139             cancelRequestLocked(previousRequest);
140         }
141     }
142 
143     /**
144      * Cancels the request with the specified {@code token} and notifies the listener of all changes
145      * to request status as a result of this operation.
146      */
cancelRequest(@onNull OverrideRequest request)147     void cancelRequest(@NonNull OverrideRequest request) {
148         // Either don't have a current request or attempting to cancel an already cancelled request
149         if (!hasRequest(request.getToken(), request.getRequestType())) {
150             return;
151         }
152         cancelCurrentRequestLocked();
153     }
154 
155     /**
156      * Cancels a request that is currently marked sticky and notifies the listener of all
157      * changes to request status as a result of this operation.
158      *
159      * @see #setStickyRequestsAllowed(boolean)
160      */
cancelStickyRequest()161     void cancelStickyRequest() {
162         if (mStickyRequest) {
163             cancelCurrentRequestLocked();
164         }
165     }
166 
167     /**
168      * Cancels the current override request, this could be due to the device being put
169      * into a hardware state that declares the flag "FLAG_CANCEL_OVERRIDE_REQUESTS"
170      */
cancelOverrideRequest()171     void cancelOverrideRequest() {
172         cancelCurrentRequestLocked();
173     }
174 
175     /**
176      * Cancels the current base state override request, this could be due to the physical
177      * configuration of the device changing.
178      */
cancelBaseStateOverrideRequest()179     void cancelBaseStateOverrideRequest() {
180         cancelCurrentBaseStateRequestLocked();
181     }
182 
183     /**
184      * Returns {@code true} if this controller is current managing a request with the specified
185      * {@code token}, {@code false} otherwise.
186      */
hasRequest(@onNull IBinder token, @OverrideRequest.OverrideRequestType int requestType)187     boolean hasRequest(@NonNull IBinder token,
188             @OverrideRequest.OverrideRequestType int requestType) {
189         if (requestType == OverrideRequest.OVERRIDE_REQUEST_TYPE_BASE_STATE) {
190             return mBaseStateRequest != null && token == mBaseStateRequest.getToken();
191         } else {
192             return mRequest != null && token == mRequest.getToken();
193         }
194     }
195 
196     /**
197      * Notifies the controller that the process with the specified {@code pid} has died. The
198      * controller will notify the listener of all changes to request status as a result of this
199      * operation.
200      */
handleProcessDied(int pid)201     void handleProcessDied(int pid) {
202         if (mBaseStateRequest != null && mBaseStateRequest.getPid() == pid) {
203             cancelCurrentBaseStateRequestLocked();
204         }
205 
206         if (mRequest != null && mRequest.getPid() == pid) {
207             if (mStickyRequestsAllowed) {
208                 // Do not cancel the requests now because sticky requests are allowed. These
209                 // requests will be cancelled on a call to cancelStickyRequests().
210                 mStickyRequest = true;
211                 return;
212             }
213             cancelCurrentRequestLocked();
214         }
215     }
216 
217     /**
218      * Notifies the controller that the base state has changed. The controller will notify the
219      * listener of all changes to request status as a result of this change.
220      */
handleBaseStateChanged(int state)221     void handleBaseStateChanged(int state) {
222         if (mBaseStateRequest != null && state != mBaseStateRequest.getRequestedState()) {
223             cancelBaseStateOverrideRequest();
224         }
225         if (mRequest == null) {
226             return;
227         }
228 
229         if ((mRequest.getFlags()
230                 & DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES) != 0) {
231             cancelCurrentRequestLocked();
232         }
233     }
234 
235     /**
236      * Notifies the controller that the set of supported states has changed. The controller will
237      * notify the listener of all changes to request status as a result of this change.
238      */
handleNewSupportedStates(int[] newSupportedStates, @DeviceStateProvider.SupportedStatesUpdatedReason int reason)239     void handleNewSupportedStates(int[] newSupportedStates,
240             @DeviceStateProvider.SupportedStatesUpdatedReason int reason) {
241         boolean isThermalCritical =
242                 reason == DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL;
243         boolean isPowerSaveEnabled =
244                 reason == DeviceStateProvider.SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_ENABLED;
245         @StatusChangedFlag int flags = 0;
246         flags |= isThermalCritical ? FLAG_THERMAL_CRITICAL : 0;
247         flags |= isPowerSaveEnabled ? FLAG_POWER_SAVE_ENABLED : 0;
248         if (mBaseStateRequest != null && !contains(newSupportedStates,
249                 mBaseStateRequest.getRequestedState())) {
250             cancelCurrentBaseStateRequestLocked(flags);
251         }
252 
253         if (mRequest != null && !contains(newSupportedStates, mRequest.getRequestedState())) {
254             cancelCurrentRequestLocked(flags);
255         }
256     }
257 
dumpInternal(PrintWriter pw)258     void dumpInternal(PrintWriter pw) {
259         OverrideRequest overrideRequest = mRequest;
260         final boolean requestActive = overrideRequest != null;
261         pw.println();
262         pw.println("Override Request active: " + requestActive);
263         if (requestActive) {
264             pw.println("Request: mPid=" + overrideRequest.getPid()
265                     + ", mRequestedState=" + overrideRequest.getRequestedState()
266                     + ", mFlags=" + overrideRequest.getFlags()
267                     + ", mStatus=" + statusToString(STATUS_ACTIVE));
268         }
269     }
270 
cancelRequestLocked(@onNull OverrideRequest requestToCancel)271     private void cancelRequestLocked(@NonNull OverrideRequest requestToCancel) {
272         cancelRequestLocked(requestToCancel, 0 /* flags */);
273     }
274 
cancelRequestLocked(@onNull OverrideRequest requestToCancel, @StatusChangedFlag int flags)275     private void cancelRequestLocked(@NonNull OverrideRequest requestToCancel,
276             @StatusChangedFlag int flags) {
277         mListener.onStatusChanged(requestToCancel, STATUS_CANCELED, flags);
278     }
279 
280     /**
281      * Handles cancelling {@code mRequest}.
282      * Notifies the listener of the canceled status as well.
283      */
cancelCurrentRequestLocked()284     private void cancelCurrentRequestLocked() {
285         cancelCurrentRequestLocked(0 /* flags */);
286     }
287 
cancelCurrentRequestLocked(@tatusChangedFlag int flags)288     private void cancelCurrentRequestLocked(@StatusChangedFlag int flags) {
289         if (mRequest == null) {
290             Slog.w(TAG, "Attempted to cancel a null OverrideRequest");
291             return;
292         }
293         mStickyRequest = false;
294         cancelRequestLocked(mRequest, flags);
295         mRequest = null;
296     }
297 
298     /**
299      * Handles cancelling {@code mBaseStateRequest}.
300      * Notifies the listener of the canceled status as well.
301      */
cancelCurrentBaseStateRequestLocked()302     private void cancelCurrentBaseStateRequestLocked() {
303         cancelCurrentBaseStateRequestLocked(0 /* flags */);
304     }
305 
cancelCurrentBaseStateRequestLocked(@tatusChangedFlag int flags)306     private void cancelCurrentBaseStateRequestLocked(@StatusChangedFlag int flags) {
307         if (mBaseStateRequest == null) {
308             Slog.w(TAG, "Attempted to cancel a null OverrideRequest");
309             return;
310         }
311         cancelRequestLocked(mBaseStateRequest, flags);
312         mBaseStateRequest = null;
313     }
314 
contains(int[] array, int value)315     private static boolean contains(int[] array, int value) {
316         for (int i = 0; i < array.length; i++) {
317             if (array[i] == value) {
318                 return true;
319             }
320         }
321         return false;
322     }
323 
324     public interface StatusChangeListener {
325 
326         /**
327          * Notifies the listener of a change in request status. If a change within the controller
328          * causes one request to become active and one to become either suspended or cancelled, this
329          * method is guaranteed to be called with the active request first before the suspended or
330          * cancelled request.
331          */
onStatusChanged(@onNull OverrideRequest request, @RequestStatus int newStatus, @StatusChangedFlag int flags)332         void onStatusChanged(@NonNull OverrideRequest request, @RequestStatus int newStatus,
333                 @StatusChangedFlag int flags);
334     }
335 }
336