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 static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.car.builtin.util.Slogf; 24 import android.car.feature.FeatureFlags; 25 import android.car.feature.FeatureFlagsImpl; 26 import android.os.FileObserver; 27 import android.os.SystemProperties; 28 import android.util.proto.ProtoOutputStream; 29 30 import com.android.car.CarLog; 31 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 32 import com.android.car.internal.util.IndentingPrintWriter; 33 import com.android.car.power.CarPowerDumpProto.SilentModeHandlerProto; 34 import com.android.internal.annotations.GuardedBy; 35 36 import libcore.io.IoUtils; 37 38 import java.io.BufferedWriter; 39 import java.io.File; 40 import java.io.FileWriter; 41 import java.io.IOException; 42 import java.nio.file.Files; 43 import java.nio.file.Paths; 44 import java.util.Objects; 45 46 /** 47 * Class to handle Silent Mode and Non-Silent Mode. 48 * 49 * <p>This monitors {@code /sys/kernel/silent_boot/pm_silentmode_hw_state} to figure out when to 50 * switch to Silent Mode and updates {@code /sys/kernel/silent_boot/pm_silentmode_kernel_state} to 51 * tell early-init services about Silent Mode change. Also, it handles forced Silent Mode for 52 * testing purpose, which is given through reboot reason. 53 */ 54 final class SilentModeHandler { 55 static final String SILENT_MODE_FORCED_SILENT = "forced-silent"; 56 static final String SILENT_MODE_FORCED_NON_SILENT = "forced-non-silent"; 57 static final String SILENT_MODE_NON_FORCED = "non-forced-silent-mode"; 58 59 private static final String TAG = CarLog.tagFor(SilentModeHandler.class); 60 61 /** 62 * The folders that are searched for sysfs files. 63 * 64 * <p>The sysfs files for Silent Mode are searched in the following order: 65 * <ol> 66 * <li>/sys/kernel/silent_boot 67 * <li>/sys/power 68 * </ol> 69 * 70 * <p>Placing the sysfs files in {@code /sys/power} is deprecated, but for backwad 71 * compatibility, we fallback to the folder when the files don't exist in 72 * {@code /sys/kernel/silent_boot}. 73 */ 74 private static final String[] SYSFS_DIRS_FOR_SILENT_MODE = 75 new String[]{"/sys/kernel/silent_boot", "/sys/power"}; 76 private static final String SYSFS_FILENAME_HW_STATE_MONITORING = "pm_silentmode_hw_state"; 77 private static final String SYSFS_FILENAME_KERNEL_SILENTMODE = "pm_silentmode_kernel_state"; 78 private static final String VALUE_SILENT_MODE = "1"; 79 private static final String VALUE_NON_SILENT_MODE = "0"; 80 private static final String SYSTEM_BOOT_REASON = "sys.boot.reason"; 81 private static final String FORCED_NON_SILENT = "reboot,forcednonsilent"; 82 private static final String FORCED_SILENT = "reboot,forcedsilent"; 83 84 private final Object mLock = new Object(); 85 private final CarPowerManagementService mService; 86 private final String mHwStateMonitoringFileName; 87 private final String mKernelSilentModeFileName; 88 89 @GuardedBy("mLock") 90 private FileObserver mFileObserver; 91 @GuardedBy("mLock") 92 private boolean mSilentModeByHwState; 93 @GuardedBy("mLock") 94 private boolean mForcedMode; 95 private boolean mSilentModeSupported; 96 97 // Allows for injecting feature flag values during testing 98 private FeatureFlags mFeatureFlags = new FeatureFlagsImpl(); 99 SilentModeHandler(@onNull CarPowerManagementService service, FeatureFlags featureFlags, @Nullable String hwStateMonitoringFileName, @Nullable String kernelSilentModeFileName, @Nullable String bootReason)100 SilentModeHandler(@NonNull CarPowerManagementService service, FeatureFlags featureFlags, 101 @Nullable String hwStateMonitoringFileName, @Nullable String kernelSilentModeFileName, 102 @Nullable String bootReason) { 103 Objects.requireNonNull(service, "CarPowerManagementService must not be null"); 104 mService = service; 105 mFeatureFlags = featureFlags; 106 String sysfsDir = searchForSysfsDir(); 107 mHwStateMonitoringFileName = hwStateMonitoringFileName == null 108 ? sysfsDir + SYSFS_FILENAME_HW_STATE_MONITORING : hwStateMonitoringFileName; 109 mKernelSilentModeFileName = kernelSilentModeFileName == null 110 ? sysfsDir + SYSFS_FILENAME_KERNEL_SILENTMODE : kernelSilentModeFileName; 111 mSilentModeSupported = fileExists(mHwStateMonitoringFileName) 112 && fileExists(mKernelSilentModeFileName); 113 String reason = bootReason; 114 if (reason == null) { 115 reason = SystemProperties.get(SYSTEM_BOOT_REASON); 116 } 117 switch (reason) { 118 case FORCED_SILENT: 119 Slogf.i(TAG, "Starting in forced silent mode"); 120 mForcedMode = true; 121 mSilentModeByHwState = true; 122 break; 123 case FORCED_NON_SILENT: 124 Slogf.i(TAG, "Starting in forced non-silent mode"); 125 mForcedMode = true; 126 mSilentModeByHwState = false; 127 break; 128 default: 129 mForcedMode = false; 130 } 131 } 132 init()133 void init() { 134 boolean forcedMode; 135 boolean silentMode; 136 synchronized (mLock) { 137 forcedMode = mForcedMode; 138 silentMode = mSilentModeByHwState; 139 } 140 if (forcedMode) { 141 updateKernelSilentMode(silentMode); 142 if (!mFeatureFlags.carPowerPolicyRefactoring()) { 143 mService.notifySilentModeChange(silentMode); 144 } 145 Slogf.i(TAG, "Now in forced mode: monitoring %s is disabled", 146 mHwStateMonitoringFileName); 147 } else { 148 startMonitoringSilentModeHwState(); 149 } 150 } 151 release()152 void release() { 153 synchronized (mLock) { 154 stopMonitoringSilentModeHwStateLocked(); 155 } 156 } 157 158 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dump(IndentingPrintWriter writer)159 void dump(IndentingPrintWriter writer) { 160 synchronized (mLock) { 161 writer.printf("mHwStateMonitoringFileName: %s\n", mHwStateMonitoringFileName); 162 writer.printf("mKernelSilentModeFileName: %s\n", mKernelSilentModeFileName); 163 writer.printf("Silent mode supported: %b\n", mSilentModeSupported); 164 writer.printf("Monitoring HW state signal: %b\n", mFileObserver != null); 165 writer.printf("Silent mode by HW state signal: %b\n", mSilentModeByHwState); 166 writer.printf("Forced silent mode: %b\n", mForcedMode); 167 } 168 } 169 170 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dumpProto(ProtoOutputStream proto)171 void dumpProto(ProtoOutputStream proto) { 172 synchronized (mLock) { 173 long silentModeHandlerToken = proto.start( 174 CarPowerDumpProto.SILENT_MODE_HANDLER); 175 proto.write(SilentModeHandlerProto.HW_STATE_MONITORING_FILE_NAME, 176 mHwStateMonitoringFileName); 177 proto.write(SilentModeHandlerProto.KERNEL_SILENT_MODE_FILE_NAME, 178 mKernelSilentModeFileName); 179 proto.write(SilentModeHandlerProto.IS_SILENT_MODE_SUPPORTED, mSilentModeSupported); 180 proto.write( 181 SilentModeHandlerProto.IS_MONITORING_HW_STATE_SIGNAL, mFileObserver != null); 182 proto.write(SilentModeHandlerProto.SILENT_MODE_BY_HW_STATE, mSilentModeByHwState); 183 proto.write(SilentModeHandlerProto.FORCED_SILENT_MODE, mForcedMode); 184 proto.end(silentModeHandlerToken); 185 } 186 } 187 isSilentMode()188 boolean isSilentMode() { 189 synchronized (mLock) { 190 return mSilentModeByHwState; 191 } 192 } 193 querySilentModeHwState()194 void querySilentModeHwState() { 195 FileObserver fileObserver; 196 synchronized (mLock) { 197 fileObserver = mFileObserver; 198 } 199 if (fileObserver != null) { 200 fileObserver.onEvent(FileObserver.MODIFY, mHwStateMonitoringFileName); 201 } 202 } 203 updateKernelSilentMode(boolean silent)204 void updateKernelSilentMode(boolean silent) { 205 try (BufferedWriter writer = 206 new BufferedWriter(new FileWriter(mKernelSilentModeFileName))) { 207 String value = silent ? VALUE_SILENT_MODE : VALUE_NON_SILENT_MODE; 208 writer.write(value); 209 writer.flush(); 210 Slogf.i(TAG, "%s is updated to %s", mKernelSilentModeFileName, value); 211 } catch (IOException e) { 212 Slogf.w(TAG, "Failed to update %s to %s", mKernelSilentModeFileName, 213 silent ? VALUE_SILENT_MODE : VALUE_NON_SILENT_MODE); 214 } 215 } 216 setSilentMode(String silentMode)217 void setSilentMode(String silentMode) { 218 switch (silentMode) { 219 case SILENT_MODE_FORCED_SILENT: 220 switchToForcedMode(true); 221 break; 222 case SILENT_MODE_FORCED_NON_SILENT: 223 switchToForcedMode(false); 224 break; 225 case SILENT_MODE_NON_FORCED: 226 switchToNonForcedMode(); 227 break; 228 default: 229 Slogf.w(TAG, "Unsupported silent mode: %s", silentMode); 230 } 231 } 232 switchToForcedMode(boolean silentMode)233 private void switchToForcedMode(boolean silentMode) { 234 boolean updated = false; 235 synchronized (mLock) { 236 if (!mForcedMode) { 237 stopMonitoringSilentModeHwStateLocked(); 238 mForcedMode = true; 239 } 240 if (mSilentModeByHwState != silentMode) { 241 mSilentModeByHwState = silentMode; 242 updated = true; 243 } 244 } 245 if (updated) { 246 updateKernelSilentMode(silentMode); 247 if (!mFeatureFlags.carPowerPolicyRefactoring()) { 248 mService.notifySilentModeChange(silentMode); 249 } 250 } 251 Slogf.i(TAG, "Now in forced %s mode: monitoring %s is disabled", 252 silentMode ? "silent" : "non-silent", mHwStateMonitoringFileName); 253 } 254 switchToNonForcedMode()255 private void switchToNonForcedMode() { 256 boolean updated = false; 257 synchronized (mLock) { 258 if (mForcedMode) { 259 Slogf.i(TAG, "Now in non forced mode: monitoring %s is started", 260 mHwStateMonitoringFileName); 261 mForcedMode = false; 262 updated = true; 263 } 264 } 265 if (updated) { 266 startMonitoringSilentModeHwState(); 267 } 268 } 269 fileExists(String filePath)270 private boolean fileExists(String filePath) { 271 return Files.exists(Paths.get(filePath)); 272 } 273 startMonitoringSilentModeHwState()274 private void startMonitoringSilentModeHwState() { 275 File monitorFile = new File(mHwStateMonitoringFileName); 276 if (!monitorFile.exists()) { 277 Slogf.w(TAG, "Failed to start monitoring Silent Mode HW state: %s doesn't exist", 278 mHwStateMonitoringFileName); 279 return; 280 } 281 FileObserver fileObserver = new FileObserver(monitorFile, FileObserver.MODIFY) { 282 @Override 283 public void onEvent(int event, String filename) { 284 boolean newSilentMode; 285 boolean oldSilentMode; 286 synchronized (mLock) { 287 // FileObserver can report events even after stopWatching is called. 288 if (mForcedMode || mFileObserver == null) { 289 return; 290 } 291 oldSilentMode = mSilentModeByHwState; 292 try { 293 String contents = IoUtils.readFileAsString(mHwStateMonitoringFileName) 294 .trim(); 295 mSilentModeByHwState = VALUE_SILENT_MODE.equals(contents); 296 Slogf.i(TAG, "%s indicates %s mode", mHwStateMonitoringFileName, 297 mSilentModeByHwState ? "silent" : "non-silent"); 298 } catch (Exception e) { 299 Slogf.w(TAG, e, "Failed to read %s", mHwStateMonitoringFileName); 300 return; 301 } 302 newSilentMode = mSilentModeByHwState; 303 } 304 if (newSilentMode != oldSilentMode) { 305 updateKernelSilentMode(newSilentMode); 306 if (!mFeatureFlags.carPowerPolicyRefactoring()) { 307 mService.notifySilentModeChange(newSilentMode); 308 } 309 } 310 } 311 }; 312 synchronized (mLock) { 313 mFileObserver = fileObserver; 314 } 315 fileObserver.startWatching(); 316 // Trigger the observer to get the initial contents 317 querySilentModeHwState(); 318 } 319 320 @GuardedBy("mLock") stopMonitoringSilentModeHwStateLocked()321 private void stopMonitoringSilentModeHwStateLocked() { 322 if (mFileObserver != null) { 323 mFileObserver.stopWatching(); 324 mFileObserver = null; 325 } 326 } 327 searchForSysfsDir()328 private static String searchForSysfsDir() { 329 for (String dir : SYSFS_DIRS_FOR_SILENT_MODE) { 330 if (Files.isDirectory(Paths.get(dir))) { 331 return dir + "/"; 332 } 333 } 334 return SYSFS_DIRS_FOR_SILENT_MODE[0] + "/"; 335 } 336 } 337