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