• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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