• 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.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 
statusToString(@equestStatus int status)62     static String statusToString(@RequestStatus int status) {
63         switch (status) {
64             case STATUS_ACTIVE:
65                 return "ACTIVE";
66             case STATUS_CANCELED:
67                 return "CANCELED";
68             case STATUS_UNKNOWN:
69                 return "UNKNOWN";
70         }
71         throw new IllegalArgumentException("Unknown status: " + status);
72     }
73 
74     private final StatusChangeListener mListener;
75 
76     // Handle to the current override request, null if none.
77     private OverrideRequest mRequest;
78     // Handle to the current base state override request, null if none.
79     private OverrideRequest mBaseStateRequest;
80 
81     private boolean mStickyRequestsAllowed;
82     // The current request has outlived their process.
83     private boolean mStickyRequest;
84 
OverrideRequestController(@onNull StatusChangeListener listener)85     OverrideRequestController(@NonNull StatusChangeListener listener) {
86         mListener = listener;
87     }
88 
89     /**
90      * Sets sticky requests as either allowed or disallowed. When sticky requests are allowed a call
91      * to {@link #handleProcessDied(int)} will not result in the request being cancelled
92      * immediately. Instead, the request will be marked sticky and must be cancelled with a call
93      * to {@link #cancelStickyRequest()}.
94      */
setStickyRequestsAllowed(boolean stickyRequestsAllowed)95     void setStickyRequestsAllowed(boolean stickyRequestsAllowed) {
96         mStickyRequestsAllowed = stickyRequestsAllowed;
97         if (!mStickyRequestsAllowed) {
98             cancelStickyRequest();
99         }
100     }
101 
102     /**
103      * Sets the new request as active and cancels the previous override request, notifies the
104      * listener of all changes to request status as a result of this operation.
105      */
addRequest(@onNull OverrideRequest request)106     void addRequest(@NonNull OverrideRequest request) {
107         OverrideRequest previousRequest = mRequest;
108         mRequest = request;
109         mListener.onStatusChanged(request, STATUS_ACTIVE);
110 
111         if (previousRequest != null) {
112             cancelRequestLocked(previousRequest);
113         }
114     }
115 
addBaseStateRequest(@onNull OverrideRequest request)116     void addBaseStateRequest(@NonNull OverrideRequest request) {
117         OverrideRequest previousRequest = mBaseStateRequest;
118         mBaseStateRequest = request;
119         mListener.onStatusChanged(request, STATUS_ACTIVE);
120 
121         if (previousRequest != null) {
122             cancelRequestLocked(previousRequest);
123         }
124     }
125 
126     /**
127      * Cancels the request with the specified {@code token} and notifies the listener of all changes
128      * to request status as a result of this operation.
129      */
cancelRequest(@onNull OverrideRequest request)130     void cancelRequest(@NonNull OverrideRequest request) {
131         // Either don't have a current request or attempting to cancel an already cancelled request
132         if (!hasRequest(request.getToken(), request.getRequestType())) {
133             return;
134         }
135         cancelCurrentRequestLocked();
136     }
137 
138     /**
139      * Cancels a request that is currently marked sticky and notifies the listener of all
140      * changes to request status as a result of this operation.
141      *
142      * @see #setStickyRequestsAllowed(boolean)
143      */
cancelStickyRequest()144     void cancelStickyRequest() {
145         if (mStickyRequest) {
146             cancelCurrentRequestLocked();
147         }
148     }
149 
150     /**
151      * Cancels the current override request, this could be due to the device being put
152      * into a hardware state that declares the flag "FLAG_CANCEL_OVERRIDE_REQUESTS"
153      */
cancelOverrideRequest()154     void cancelOverrideRequest() {
155         cancelCurrentRequestLocked();
156     }
157 
158     /**
159      * Cancels the current base state override request, this could be due to the physical
160      * configuration of the device changing.
161      */
cancelBaseStateOverrideRequest()162     void cancelBaseStateOverrideRequest() {
163         cancelCurrentBaseStateRequestLocked();
164     }
165 
166     /**
167      * Returns {@code true} if this controller is current managing a request with the specified
168      * {@code token}, {@code false} otherwise.
169      */
hasRequest(@onNull IBinder token, @OverrideRequest.OverrideRequestType int requestType)170     boolean hasRequest(@NonNull IBinder token,
171             @OverrideRequest.OverrideRequestType int requestType) {
172         if (requestType == OverrideRequest.OVERRIDE_REQUEST_TYPE_BASE_STATE) {
173             return mBaseStateRequest != null && token == mBaseStateRequest.getToken();
174         } else {
175             return mRequest != null && token == mRequest.getToken();
176         }
177     }
178 
179     /**
180      * Notifies the controller that the process with the specified {@code pid} has died. The
181      * controller will notify the listener of all changes to request status as a result of this
182      * operation.
183      */
handleProcessDied(int pid)184     void handleProcessDied(int pid) {
185         if (mBaseStateRequest != null && mBaseStateRequest.getPid() == pid) {
186             cancelCurrentBaseStateRequestLocked();
187         }
188 
189         if (mRequest != null && mRequest.getPid() == pid) {
190             if (mStickyRequestsAllowed) {
191                 // Do not cancel the requests now because sticky requests are allowed. These
192                 // requests will be cancelled on a call to cancelStickyRequests().
193                 mStickyRequest = true;
194                 return;
195             }
196             cancelCurrentRequestLocked();
197         }
198     }
199 
200     /**
201      * Notifies the controller that the base state has changed. The controller will notify the
202      * listener of all changes to request status as a result of this change.
203      */
handleBaseStateChanged(int state)204     void handleBaseStateChanged(int state) {
205         if (mBaseStateRequest != null && state != mBaseStateRequest.getRequestedState()) {
206             cancelBaseStateOverrideRequest();
207         }
208         if (mRequest == null) {
209             return;
210         }
211 
212         if ((mRequest.getFlags()
213                 & DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES) != 0) {
214             cancelCurrentRequestLocked();
215         }
216     }
217 
218     /**
219      * Notifies the controller that the set of supported states has changed. The controller will
220      * notify the listener of all changes to request status as a result of this change.
221      */
handleNewSupportedStates(int[] newSupportedStates)222     void handleNewSupportedStates(int[] newSupportedStates) {
223         if (mBaseStateRequest != null && !contains(newSupportedStates,
224                 mBaseStateRequest.getRequestedState())) {
225             cancelCurrentBaseStateRequestLocked();
226         }
227 
228         if (mRequest != null && !contains(newSupportedStates, mRequest.getRequestedState())) {
229             cancelCurrentRequestLocked();
230         }
231     }
232 
dumpInternal(PrintWriter pw)233     void dumpInternal(PrintWriter pw) {
234         OverrideRequest overrideRequest = mRequest;
235         final boolean requestActive = overrideRequest != null;
236         pw.println();
237         pw.println("Override Request active: " + requestActive);
238         if (requestActive) {
239             pw.println("Request: mPid=" + overrideRequest.getPid()
240                     + ", mRequestedState=" + overrideRequest.getRequestedState()
241                     + ", mFlags=" + overrideRequest.getFlags()
242                     + ", mStatus=" + statusToString(STATUS_ACTIVE));
243         }
244     }
245 
cancelRequestLocked(@onNull OverrideRequest requestToCancel)246     private void cancelRequestLocked(@NonNull OverrideRequest requestToCancel) {
247         mListener.onStatusChanged(requestToCancel, STATUS_CANCELED);
248     }
249 
250     /**
251      * Handles cancelling {@code mRequest}.
252      * Notifies the listener of the canceled status as well.
253      */
cancelCurrentRequestLocked()254     private void cancelCurrentRequestLocked() {
255         if (mRequest == null) {
256             Slog.w(TAG, "Attempted to cancel a null OverrideRequest");
257             return;
258         }
259         mStickyRequest = false;
260         cancelRequestLocked(mRequest);
261         mRequest = null;
262     }
263 
264     /**
265      * Handles cancelling {@code mBaseStateRequest}.
266      * Notifies the listener of the canceled status as well.
267      */
cancelCurrentBaseStateRequestLocked()268     private void cancelCurrentBaseStateRequestLocked() {
269         if (mBaseStateRequest == null) {
270             Slog.w(TAG, "Attempted to cancel a null OverrideRequest");
271             return;
272         }
273         cancelRequestLocked(mBaseStateRequest);
274         mBaseStateRequest = null;
275     }
276 
contains(int[] array, int value)277     private static boolean contains(int[] array, int value) {
278         for (int i = 0; i < array.length; i++) {
279             if (array[i] == value) {
280                 return true;
281             }
282         }
283         return false;
284     }
285 
286     public interface StatusChangeListener {
287         /**
288          * Notifies the listener of a change in request status. If a change within the controller
289          * causes one request to become active and one to become either suspended or cancelled, this
290          * method is guaranteed to be called with the active request first before the suspended or
291          * cancelled request.
292          */
onStatusChanged(@onNull OverrideRequest request, @RequestStatus int newStatus)293         void onStatusChanged(@NonNull OverrideRequest request, @RequestStatus int newStatus);
294     }
295 }
296