1 /* 2 * Copyright (C) 2024 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 android.car.testapi; 18 19 import android.annotation.Nullable; 20 import android.automotive.powerpolicy.internal.ICarPowerPolicyDelegate; 21 import android.automotive.powerpolicy.internal.ICarPowerPolicyDelegateCallback; 22 import android.automotive.powerpolicy.internal.PowerPolicyInitData; 23 import android.car.hardware.power.PowerComponent; 24 import android.car.hardware.power.PowerComponentUtil; 25 import android.frameworks.automotive.powerpolicy.CarPowerPolicy; 26 import android.os.FileObserver; 27 import android.os.Handler; 28 import android.os.HandlerThread; 29 import android.os.RemoteException; 30 import android.util.ArrayMap; 31 import android.util.IntArray; 32 import android.util.Log; 33 import android.util.SparseArray; 34 35 import libcore.io.IoUtils; 36 37 import java.io.File; 38 import java.util.ArrayList; 39 import java.util.List; 40 import java.util.Map; 41 import java.util.Objects; 42 43 /** 44 * Fake power policy daemon to be used in car service test and car service unit test when 45 * refactored power policy logic is used. 46 */ 47 public final class FakeRefactoredCarPowerPolicyDaemon extends ICarPowerPolicyDelegate.Default { 48 private static final String TAG = FakeRefactoredCarPowerPolicyDaemon.class.getSimpleName(); 49 50 /** 51 * The power policy ID for the system power policy for all power components on; the default 52 * power policy for the "ON" state. 53 */ 54 public static final String SYSTEM_POWER_POLICY_ALL_ON = "system_power_policy_all_on"; 55 /** 56 * The power policy ID for the system power policy for no user interaction; the default 57 * power policy for silent mode and shutdown preparation states. 58 */ 59 public static final String SYSTEM_POWER_POLICY_NO_USER_INTERACTION = 60 "system_power_policy_no_user_interaction"; 61 /** 62 * The power policy ID for the system power policy for initial on power components; the default 63 * power policy for the "WAIT_FOR_VHAL" state. 64 */ 65 public static final String SYSTEM_POWER_POLICY_INITIAL_ON = "system_power_policy_initial_on"; 66 /** 67 * The power policy ID for the system power policy for suspend preparation. 68 */ 69 public static final String SYSTEM_POWER_POLICY_SUSPEND_PREP = 70 "system_power_policy_suspend_prep"; 71 72 private static final String POLICY_PER_STATE_GROUP_ID = "default_policy_per_state"; 73 74 private final int[] mCustomComponents; 75 private final FileObserver mFileObserver; 76 private final ComponentHandler mComponentHandler = new ComponentHandler(); 77 private final HandlerThread mHandlerThread = new HandlerThread(TAG); 78 private final Map<String, CarPowerPolicy> mPolicies = new ArrayMap<>(); 79 private final Map<String, SparseArray<CarPowerPolicy>> mPowerPolicyGroups = new ArrayMap<>(); 80 81 private String mLastSetPowerPolicyGroupId = POLICY_PER_STATE_GROUP_ID; 82 83 private int mLastNotifiedPowerState; 84 private boolean mSilentModeOn; 85 private String mPendingPowerPolicyId; 86 private String mLastDefinedPolicyId; 87 private String mCurrentPowerPolicyId = SYSTEM_POWER_POLICY_INITIAL_ON; 88 private Handler mHandler; 89 private ICarPowerPolicyDelegateCallback mCallback; 90 private File mFileKernelSilentMode; 91 FakeRefactoredCarPowerPolicyDaemon(@ullable File fileKernelSilentMode, @Nullable int[] customComponents)92 public FakeRefactoredCarPowerPolicyDaemon(@Nullable File fileKernelSilentMode, 93 @Nullable int[] customComponents) throws Exception { 94 mHandlerThread.start(); 95 mHandler = new Handler(mHandlerThread.getLooper()); 96 mFileKernelSilentMode = (fileKernelSilentMode == null) 97 ? new File("KERNEL_SILENT_MODE") : fileKernelSilentMode; 98 mFileObserver = new SilentModeFileObserver(mFileKernelSilentMode, FileObserver.CLOSE_WRITE); 99 CarPowerPolicy policyInitialOn = createInitialOnPowerPolicy(); 100 CarPowerPolicy policyAllOn = createAllOnPowerPolicy(); 101 CarPowerPolicy policyNoUser = createNoUserPowerPolicy(); 102 CarPowerPolicy policySuspendPrep = createSuspendPrepPowerPolicy(); 103 mPolicies.put(SYSTEM_POWER_POLICY_INITIAL_ON, policyInitialOn); 104 mPolicies.put(SYSTEM_POWER_POLICY_ALL_ON, policyAllOn); 105 mPolicies.put(SYSTEM_POWER_POLICY_NO_USER_INTERACTION, policyNoUser); 106 mPolicies.put(SYSTEM_POWER_POLICY_SUSPEND_PREP, policySuspendPrep); 107 mPowerPolicyGroups.put(POLICY_PER_STATE_GROUP_ID, createPolicyGroup( 108 SYSTEM_POWER_POLICY_INITIAL_ON, SYSTEM_POWER_POLICY_ALL_ON)); 109 mCustomComponents = Objects.requireNonNullElse(customComponents, new int[]{}); 110 } 111 createPolicy( String policyId, int[] enabledComponents, int[] disabledComponents)112 private static CarPowerPolicy createPolicy( 113 String policyId, int[] enabledComponents, int[] disabledComponents) { 114 CarPowerPolicy policy = new CarPowerPolicy(); 115 policy.policyId = policyId; 116 policy.enabledComponents = enabledComponents; 117 policy.disabledComponents = disabledComponents; 118 return policy; 119 } 120 121 // Create a fake system_power_policy_initial_on createInitialOnPowerPolicy()122 private static CarPowerPolicy createInitialOnPowerPolicy() { 123 return createPolicy(SYSTEM_POWER_POLICY_INITIAL_ON, 124 new int[]{PowerComponent.AUDIO, PowerComponent.DISPLAY, PowerComponent.CPU}, 125 new int[]{PowerComponent.MEDIA, PowerComponent.BLUETOOTH, 126 PowerComponent.WIFI, PowerComponent.CELLULAR, 127 PowerComponent.ETHERNET, PowerComponent.PROJECTION, 128 PowerComponent.NFC, PowerComponent.INPUT, 129 PowerComponent.VOICE_INTERACTION, 130 PowerComponent.VISUAL_INTERACTION, 131 PowerComponent.TRUSTED_DEVICE_DETECTION, 132 PowerComponent.LOCATION, PowerComponent.MICROPHONE}); 133 } 134 135 // Create a fake system_power_policy_all_on createAllOnPowerPolicy()136 private static CarPowerPolicy createAllOnPowerPolicy() { 137 return createPolicy(SYSTEM_POWER_POLICY_ALL_ON, new int[]{PowerComponent.AUDIO, 138 PowerComponent.MEDIA, PowerComponent.DISPLAY, PowerComponent.BLUETOOTH, 139 PowerComponent.WIFI, PowerComponent.CELLULAR, PowerComponent.ETHERNET, 140 PowerComponent.PROJECTION, PowerComponent.NFC, PowerComponent.INPUT, 141 PowerComponent.VOICE_INTERACTION, PowerComponent.VISUAL_INTERACTION, 142 PowerComponent.TRUSTED_DEVICE_DETECTION, PowerComponent.LOCATION, 143 PowerComponent.MICROPHONE, PowerComponent.CPU}, new int[]{}); 144 } 145 146 // Create a fake system_power_policy_no_user_interaction createNoUserPowerPolicy()147 private static CarPowerPolicy createNoUserPowerPolicy() { 148 return createPolicy(SYSTEM_POWER_POLICY_NO_USER_INTERACTION, 149 new int[]{ 150 PowerComponent.WIFI, PowerComponent.CELLULAR, PowerComponent.ETHERNET, 151 PowerComponent.CPU, PowerComponent.TRUSTED_DEVICE_DETECTION}, 152 new int[]{ 153 PowerComponent.AUDIO, PowerComponent.MEDIA, PowerComponent.DISPLAY, 154 PowerComponent.BLUETOOTH, PowerComponent.PROJECTION, PowerComponent.NFC, 155 PowerComponent.INPUT, PowerComponent.VOICE_INTERACTION, 156 PowerComponent.VISUAL_INTERACTION, PowerComponent.LOCATION, 157 PowerComponent.MICROPHONE}); 158 } 159 160 // Create a fake system_power_policy_suspend_prep createSuspendPrepPowerPolicy()161 private static CarPowerPolicy createSuspendPrepPowerPolicy() { 162 return createPolicy(SYSTEM_POWER_POLICY_SUSPEND_PREP, new int[]{}, new int[]{ 163 PowerComponent.AUDIO, PowerComponent.BLUETOOTH, PowerComponent.WIFI, 164 PowerComponent.LOCATION, PowerComponent.MICROPHONE, PowerComponent.CPU}); 165 } 166 createPolicyGroup(String waitForVhalPolicyId, String onPolicyId)167 private SparseArray<CarPowerPolicy> createPolicyGroup(String waitForVhalPolicyId, 168 String onPolicyId) { 169 SparseArray<CarPowerPolicy> policyGroup = new SparseArray<>(); 170 policyGroup.put(PowerState.WAIT_FOR_VHAL, mPolicies.get(waitForVhalPolicyId)); 171 policyGroup.put(PowerState.ON, mPolicies.get(onPolicyId)); 172 return policyGroup; 173 } 174 175 @Override notifyCarServiceReady(ICarPowerPolicyDelegateCallback callback)176 public PowerPolicyInitData notifyCarServiceReady(ICarPowerPolicyDelegateCallback callback) { 177 Log.i(TAG, "Fake refactored CPPD was notified that car service is ready"); 178 mCallback = callback; 179 PowerPolicyInitData initData = new PowerPolicyInitData(); 180 initData.currentPowerPolicy = mPolicies.get(mCurrentPowerPolicyId); 181 initData.registeredPolicies = new CarPowerPolicy[]{ 182 mPolicies.get(SYSTEM_POWER_POLICY_INITIAL_ON), 183 mPolicies.get(SYSTEM_POWER_POLICY_ALL_ON), 184 mPolicies.get(SYSTEM_POWER_POLICY_NO_USER_INTERACTION), 185 mPolicies.get(SYSTEM_POWER_POLICY_SUSPEND_PREP)}; 186 initData.registeredCustomComponents = mCustomComponents; 187 mComponentHandler.applyPolicy(mPolicies.get(mCurrentPowerPolicyId)); 188 return initData; 189 } 190 191 @Override applyPowerPolicyAsync(int requestId, String policyId, boolean force)192 public void applyPowerPolicyAsync(int requestId, String policyId, boolean force) 193 throws RemoteException { 194 Log.i(TAG, "Fake refactored CPPD is attempting to apply power policy " + policyId); 195 if (mCallback == null) { 196 throw new IllegalStateException("Fake refactored CPPD callback is null, was " 197 + "notifyCarServiceReady() called?"); 198 } 199 boolean deferred = isPreemptivePolicy(mCurrentPowerPolicyId) 200 && !isPreemptivePolicy(policyId); 201 CarPowerPolicy currentPolicy = mPolicies.get(policyId); 202 if (currentPolicy == null) { 203 throw new IllegalArgumentException("Power policy " + policyId + " is invalid"); 204 } 205 mComponentHandler.applyPolicy(currentPolicy); 206 CarPowerPolicy accumulatedPolicy = mComponentHandler.getAccumulatedPolicy(policyId); 207 mCallback.updatePowerComponents(accumulatedPolicy); 208 mCallback.onApplyPowerPolicySucceeded(requestId, accumulatedPolicy, deferred); 209 if (!deferred) { 210 mCurrentPowerPolicyId = policyId; 211 } 212 } 213 214 @Override applyPowerPolicyPerPowerStateChangeAsync(int requestId, int state)215 public void applyPowerPolicyPerPowerStateChangeAsync(int requestId, int state) 216 throws RemoteException { 217 CarPowerPolicy policy = mPowerPolicyGroups.get(mLastSetPowerPolicyGroupId).get(state); 218 if (policy == null) { 219 throw new IllegalArgumentException("No default policy defined for state " + state); 220 } 221 if (mSilentModeOn) { 222 mPendingPowerPolicyId = policy.policyId; 223 Log.d(TAG, "Silent mode is on, so applying power policy for state " + state 224 + " is deferred, setting pending power policy to " + mPendingPowerPolicyId); 225 mCallback.onApplyPowerPolicySucceeded(requestId, policy, /* deferred= */ true); 226 return; 227 } 228 mHandler.post(() -> { 229 try { 230 mCallback.updatePowerComponents(policy); 231 mCallback.onApplyPowerPolicySucceeded(requestId, policy, /* deferred= */ false); 232 mLastNotifiedPowerState = state; 233 mComponentHandler.applyPolicy(policy); 234 mCurrentPowerPolicyId = policy.policyId; 235 } catch (Exception e) { 236 Log.w(TAG, "Cannot call onApplyPowerPolicySucceeded", e); 237 } 238 }); 239 } 240 applyPowerPolicyInternal(String policyId, String errMsg)241 private void applyPowerPolicyInternal(String policyId, String errMsg) { 242 mComponentHandler.applyPolicy(mPolicies.get(policyId)); 243 CarPowerPolicy accumulatedPolicy = mComponentHandler.getAccumulatedPolicy(policyId); 244 try { 245 mCallback.onPowerPolicyChanged(accumulatedPolicy); 246 mCallback.updatePowerComponents(accumulatedPolicy); 247 mCurrentPowerPolicyId = policyId; 248 } catch (RemoteException e) { 249 Log.d(TAG, errMsg, e); 250 } 251 } 252 253 @Override setPowerPolicyGroup(String policyGroupId)254 public void setPowerPolicyGroup(String policyGroupId) { 255 if (mPowerPolicyGroups.get(policyGroupId) == null) { 256 throw new IllegalArgumentException("Policy group " + policyGroupId + " undefined"); 257 } 258 } 259 convertIntIterableToArray(Iterable<Integer> iterable)260 private int[] convertIntIterableToArray(Iterable<Integer> iterable) { 261 List<Integer> list = new ArrayList<>(); 262 iterable.forEach(list::add); 263 return list.stream().mapToInt(i->i).toArray(); 264 } 265 266 @Override notifyPowerPolicyDefinition(String policyId, String[] enabledComponents, String[] disabledComponents)267 public void notifyPowerPolicyDefinition(String policyId, String[] enabledComponents, 268 String[] disabledComponents) { 269 mLastDefinedPolicyId = policyId; 270 CarPowerPolicy policy = new CarPowerPolicy(); 271 policy.policyId = policyId; 272 policy.enabledComponents = convertIntIterableToArray( 273 PowerComponentUtil.toPowerComponents(List.of(enabledComponents), 274 /* prefix= */ false)); 275 policy.disabledComponents = convertIntIterableToArray( 276 PowerComponentUtil.toPowerComponents(List.of(disabledComponents), 277 /* prefix= */ false)); 278 mPolicies.put(policyId, policy); 279 } 280 281 @Override notifyPowerPolicyGroupDefinition( String policyGroupId, String[] powerPolicyPerState)282 public void notifyPowerPolicyGroupDefinition( 283 String policyGroupId, String[] powerPolicyPerState) { 284 String waitForVhalPolicyId = powerPolicyPerState[0]; 285 if (mPolicies.get(waitForVhalPolicyId) == null) { 286 throw new IllegalArgumentException( 287 "No registered policy with ID " + waitForVhalPolicyId); 288 } 289 String onPolicyId = powerPolicyPerState[1]; 290 if (mPolicies.get(onPolicyId) == null) { 291 throw new IllegalArgumentException( 292 "No registered policy with ID " + onPolicyId); 293 } 294 mPowerPolicyGroups.put( 295 policyGroupId, createPolicyGroup(waitForVhalPolicyId, onPolicyId)); 296 SparseArray<CarPowerPolicy> policyGroup = new SparseArray<>(); 297 policyGroup.put(PowerState.WAIT_FOR_VHAL, mPolicies.get(waitForVhalPolicyId)); 298 policyGroup.put(PowerState.ON, mPolicies.get(onPolicyId)); 299 mPowerPolicyGroups.put(policyGroupId, policyGroup); 300 } 301 302 /** 303 * Get the last power state notified to the daemon 304 * @return Last notified power state 305 */ getLastNotifiedPowerState()306 public int getLastNotifiedPowerState() { 307 return mLastNotifiedPowerState; 308 } 309 getLastDefinedPolicyId()310 public String getLastDefinedPolicyId() { 311 return mLastDefinedPolicyId; 312 } 313 getCurrentPowerPolicyId()314 public String getCurrentPowerPolicyId() { 315 return mCurrentPowerPolicyId; 316 } 317 318 /** 319 * Begin observing the silent mode file for changes in silent mode state. Should be called 320 * at the beginning of a test case that involves silent mode and power policy. 321 */ silentModeFileObserverStartWatching()322 public void silentModeFileObserverStartWatching() { 323 mFileObserver.startWatching(); 324 } 325 326 /** 327 * Stop observing the silent mode file. Should be called at the end of a test case where 328 * {@link #silentModeFileObserverStopWatching()} was called. 329 */ silentModeFileObserverStopWatching()330 public void silentModeFileObserverStopWatching() { 331 mFileObserver.stopWatching(); 332 } 333 334 @Override getInterfaceVersion()335 public int getInterfaceVersion() { 336 return ICarPowerPolicyDelegate.VERSION; 337 } 338 339 @Override getInterfaceHash()340 public String getInterfaceHash() { 341 return ICarPowerPolicyDelegate.HASH; 342 } 343 isPreemptivePolicy(String policyId)344 private boolean isPreemptivePolicy(String policyId) { 345 return Objects.equals(policyId, SYSTEM_POWER_POLICY_NO_USER_INTERACTION) 346 || Objects.equals(policyId, SYSTEM_POWER_POLICY_SUSPEND_PREP); 347 } 348 349 private final class SilentModeFileObserver extends FileObserver { SilentModeFileObserver(File file, int mask)350 SilentModeFileObserver(File file, int mask) { 351 super(file, mask); 352 } 353 354 @Override onEvent(int event, @Nullable String path)355 public void onEvent(int event, @Nullable String path) { 356 boolean silent; 357 try { 358 String contents = IoUtils.readFileAsString( 359 mFileKernelSilentMode.getPath().toString()).trim(); 360 silent = contents.equals("1"); 361 } catch (Exception e) { 362 Log.w(TAG, "Fake CPPD couldn't read kernel silent mode file"); 363 return; 364 } 365 Log.d(TAG, "Fake CPPD observed change in silent mode kernel file, silent = " 366 + silent); 367 if (silent) { 368 mSilentModeOn = true; 369 Log.d(TAG, "Fake CPPD attempting to apply silent power policy"); 370 applyPowerPolicyInternal(SYSTEM_POWER_POLICY_NO_USER_INTERACTION, 371 "Fake CPPD failed to apply silent power policy"); 372 } else { 373 mSilentModeOn = false; 374 Log.d(TAG, "Fake CPPD attempting to apply pending power policy"); 375 if (mPendingPowerPolicyId != null) { 376 applyPowerPolicyInternal(mPendingPowerPolicyId, 377 "Fake CPPD failed to apply pending power policy"); 378 } 379 } 380 } 381 } 382 383 private static final class ComponentHandler { 384 private final IntArray mEnabledComponents = new IntArray(); 385 private final IntArray mDisabledComponents = new IntArray(); 386 getAccumulatedPolicy(String policyId)387 CarPowerPolicy getAccumulatedPolicy(String policyId) { 388 CarPowerPolicy policy = new CarPowerPolicy(); 389 policy.policyId = policyId; 390 policy.enabledComponents = mEnabledComponents.toArray(); 391 policy.disabledComponents = mDisabledComponents.toArray(); 392 return policy; 393 } 394 applyPolicy(CarPowerPolicy policy)395 void applyPolicy(CarPowerPolicy policy) { 396 int[] enabledComponents = policy.enabledComponents; 397 for (int i = 0; i < enabledComponents.length; i++) { 398 int component = enabledComponents[i]; 399 if (!mEnabledComponents.contains(component)) { 400 if (mDisabledComponents.contains(component)) { 401 mDisabledComponents.remove(mDisabledComponents.indexOf(component)); 402 } 403 mEnabledComponents.add(component); 404 } 405 } 406 int[] disabledComponents = policy.disabledComponents; 407 for (int i = 0; i < disabledComponents.length; i++) { 408 int component = disabledComponents[i]; 409 if (!mDisabledComponents.contains(component)) { 410 if (mEnabledComponents.contains(component)) { 411 mEnabledComponents.remove(mEnabledComponents.indexOf(component)); 412 } 413 mDisabledComponents.add(component); 414 } 415 } 416 } 417 } 418 } 419