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