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