• 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.car.power;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.os.FileObserver;
22 import android.os.SystemProperties;
23 import android.util.IndentingPrintWriter;
24 
25 import com.android.car.CarLog;
26 import com.android.internal.annotations.GuardedBy;
27 import com.android.internal.annotations.VisibleForTesting;
28 import com.android.server.utils.Slogf;
29 
30 import libcore.io.IoUtils;
31 
32 import java.io.BufferedWriter;
33 import java.io.File;
34 import java.io.FileWriter;
35 import java.io.IOException;
36 import java.util.Objects;
37 
38 /**
39  * Class to handle Silent Mode and Non-Silent Mode.
40  *
41  * <p>This monitors {@code /sys/power/pm_silentmode_hw_state} to figure out when to switch to Silent
42  * Mode and updates {@code /sys/power/pm_silentmode_kernel} to tell early-init services about Silent
43  * Mode change. Also, it handles forced Silent Mode for testing purpose, which is given through
44  * reboot reason.
45  */
46 final class SilentModeHandler {
47     static final String SILENT_MODE_FORCED_SILENT = "forced-silent";
48     static final String SILENT_MODE_FORCED_NON_SILENT = "forced-non-silent";
49     static final String SILENT_MODE_NON_FORCED = "non-forced-silent-mode";
50 
51     private static final String TAG = CarLog.tagFor(SilentModeHandler.class);
52 
53     private static final String SYSFS_FILENAME_HW_STATE_MONITORING =
54             "/sys/power/pm_silentmode_hw_state";
55     private static final String SYSFS_FILENAME_KERNEL_SILENTMODE =
56             "/sys/power/pm_silentmode_kernel";
57     private static final String VALUE_SILENT_MODE = "1";
58     private static final String VALUE_NON_SILENT_MODE = "0";
59     private static final String SYSTEM_BOOT_REASON = "sys.boot.reason";
60     private static final String FORCED_NON_SILENT = "reboot,forcednonsilent";
61     private static final String FORCED_SILENT = "reboot,forcedsilent";
62 
63     private final Object mLock = new Object();
64     private final CarPowerManagementService mService;
65     private final String mHwStateMonitoringFileName;
66     private final String mKernelSilentModeFileName;
67 
68     @GuardedBy("mLock")
69     private FileObserver mFileObserver;
70     @GuardedBy("mLock")
71     private boolean mSilentModeByHwState;
72     @GuardedBy("mLock")
73     private boolean mForcedMode;
74 
75     @VisibleForTesting
SilentModeHandler(@onNull CarPowerManagementService service, @Nullable String hwStateMonitoringFileName, @Nullable String kernelSilentModeFileName, @Nullable String bootReason)76     SilentModeHandler(@NonNull CarPowerManagementService service,
77             @Nullable String hwStateMonitoringFileName, @Nullable String kernelSilentModeFileName,
78             @Nullable String bootReason) {
79         Objects.requireNonNull(service, "CarPowerManagementService must not be null");
80         mService = service;
81         mHwStateMonitoringFileName = hwStateMonitoringFileName == null
82                 ? SYSFS_FILENAME_HW_STATE_MONITORING
83                 : hwStateMonitoringFileName;
84         mKernelSilentModeFileName = kernelSilentModeFileName == null
85                 ? SYSFS_FILENAME_KERNEL_SILENTMODE
86                 : kernelSilentModeFileName;
87         if (bootReason == null) {
88             bootReason = SystemProperties.get(SYSTEM_BOOT_REASON);
89         }
90         switch (bootReason) {
91             case FORCED_SILENT:
92                 Slogf.i(TAG, "Starting in forced silent mode");
93                 mForcedMode = true;
94                 mSilentModeByHwState = true;
95                 break;
96             case FORCED_NON_SILENT:
97                 Slogf.i(TAG, "Starting in forced non-silent mode");
98                 mForcedMode = true;
99                 mSilentModeByHwState = false;
100                 break;
101             default:
102                 mForcedMode = false;
103         }
104     }
105 
init()106     void init() {
107         boolean forcedMode;
108         boolean silentMode;
109         synchronized (mLock) {
110             forcedMode = mForcedMode;
111             silentMode = mSilentModeByHwState;
112         }
113         if (forcedMode) {
114             updateKernelSilentMode(silentMode);
115             mService.notifySilentModeChange(silentMode);
116             Slogf.i(TAG, "Now in forced mode: monitoring %s is disabled",
117                     mHwStateMonitoringFileName);
118         } else {
119             startMonitoringSilentModeHwState();
120         }
121     }
122 
release()123     void release() {
124         synchronized (mLock) {
125             stopMonitoringSilentModeHwStateLocked();
126         }
127     }
128 
dump(IndentingPrintWriter writer)129     void dump(IndentingPrintWriter writer) {
130         synchronized (mLock) {
131             writer.printf("Monitoring HW state signal: %b\n", mFileObserver != null);
132             writer.printf("Silent mode by HW state signal: %b\n", mSilentModeByHwState);
133             writer.printf("Forced silent mode: %b\n", mForcedMode);
134         }
135     }
136 
isSilentMode()137     boolean isSilentMode() {
138         synchronized (mLock) {
139             return mSilentModeByHwState;
140         }
141     }
142 
querySilentModeHwState()143     void querySilentModeHwState() {
144         FileObserver fileObserver;
145         synchronized (mLock) {
146             fileObserver = mFileObserver;
147         }
148         if (fileObserver != null) {
149             fileObserver.onEvent(FileObserver.MODIFY, mHwStateMonitoringFileName);
150         }
151     }
152 
updateKernelSilentMode(boolean silent)153     void updateKernelSilentMode(boolean silent) {
154         try (BufferedWriter writer =
155                 new BufferedWriter(new FileWriter(mKernelSilentModeFileName))) {
156             String value = silent ? VALUE_SILENT_MODE : VALUE_NON_SILENT_MODE;
157             writer.write(value);
158             writer.flush();
159             Slogf.i(TAG, "%s is updated to %s", mKernelSilentModeFileName, value);
160         } catch (IOException e) {
161             Slogf.w(TAG, "Failed to update %s to %s", mKernelSilentModeFileName,
162                     silent ? VALUE_SILENT_MODE : VALUE_NON_SILENT_MODE);
163         }
164     }
165 
setSilentMode(String silentMode)166     void setSilentMode(String silentMode) {
167         switch (silentMode) {
168             case SILENT_MODE_FORCED_SILENT:
169                 switchToForcedMode(true);
170                 break;
171             case SILENT_MODE_FORCED_NON_SILENT:
172                 switchToForcedMode(false);
173                 break;
174             case SILENT_MODE_NON_FORCED:
175                 switchToNonForcedMode();
176                 break;
177             default:
178                 Slogf.w(TAG, "Unsupported silent mode: %s", silentMode);
179         }
180     }
181 
switchToForcedMode(boolean silentMode)182     private void switchToForcedMode(boolean silentMode) {
183         boolean updated = false;
184         synchronized (mLock) {
185             if (!mForcedMode) {
186                 stopMonitoringSilentModeHwStateLocked();
187                 mForcedMode = true;
188             }
189             if (mSilentModeByHwState != silentMode) {
190                 mSilentModeByHwState = silentMode;
191                 updated = true;
192             }
193         }
194         if (updated) {
195             updateKernelSilentMode(silentMode);
196             mService.notifySilentModeChange(silentMode);
197         }
198         Slogf.i(TAG, "Now in forced %s mode: monitoring %s is disabled",
199                 silentMode ? "silent" : "non-silent", mHwStateMonitoringFileName);
200     }
201 
switchToNonForcedMode()202     private void switchToNonForcedMode() {
203         boolean updated = false;
204         synchronized (mLock) {
205             if (mForcedMode) {
206                 Slogf.i(TAG, "Now in non forced mode: monitoring %s is started",
207                         mHwStateMonitoringFileName);
208                 mForcedMode = false;
209                 updated = true;
210             }
211         }
212         if (updated) {
213             startMonitoringSilentModeHwState();
214         }
215     }
216 
startMonitoringSilentModeHwState()217     private void startMonitoringSilentModeHwState() {
218         File monitorFile = new File(mHwStateMonitoringFileName);
219         if (!monitorFile.exists()) {
220             Slogf.w(TAG, "Failed to start monitoring Silent Mode HW state: %s doesn't exist",
221                     mHwStateMonitoringFileName);
222             return;
223         }
224         FileObserver fileObserver = new FileObserver(monitorFile, FileObserver.MODIFY) {
225             @Override
226             public void onEvent(int event, String filename) {
227                 boolean newSilentMode;
228                 boolean oldSilentMode;
229                 synchronized (mLock) {
230                     // FileObserver can report events even after stopWatching is called. To ignore
231                     // such events, check the current internal state.
232                     if (mForcedMode) {
233                         return;
234                     }
235                     oldSilentMode = mSilentModeByHwState;
236                     try {
237                         String contents = IoUtils.readFileAsString(mHwStateMonitoringFileName)
238                                 .trim();
239                         mSilentModeByHwState = VALUE_SILENT_MODE.equals(contents);
240                         Slogf.i(TAG, "%s indicates %s mode", mHwStateMonitoringFileName,
241                                 mSilentModeByHwState ? "silent" : "non-silent");
242                     } catch (Exception e) {
243                         Slogf.w(TAG, e, "Failed to read %s", mHwStateMonitoringFileName);
244                         return;
245                     }
246                     newSilentMode = mSilentModeByHwState;
247                 }
248                 if (newSilentMode != oldSilentMode) {
249                     updateKernelSilentMode(newSilentMode);
250                     mService.notifySilentModeChange(newSilentMode);
251                 }
252             }
253         };
254         synchronized (mLock) {
255             mFileObserver = fileObserver;
256         }
257         fileObserver.startWatching();
258         // Trigger the observer to get the initial contents
259         querySilentModeHwState();
260     }
261 
stopMonitoringSilentModeHwStateLocked()262     private void stopMonitoringSilentModeHwStateLocked() {
263         if (mFileObserver != null) {
264             mFileObserver.stopWatching();
265             mFileObserver = null;
266         }
267     }
268 }
269