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.systeminterface; 18 19 import android.annotation.NonNull; 20 import android.car.builtin.util.Slogf; 21 22 import com.android.internal.annotations.VisibleForTesting; 23 24 import libcore.io.IoUtils; 25 26 import java.io.BufferedReader; 27 import java.io.BufferedWriter; 28 import java.io.FileReader; 29 import java.io.FileWriter; 30 import java.io.IOException; 31 32 /** 33 * Uses sysfs(sys/power/state) to force device into suspend 34 */ 35 public final class SystemPowerControlHelper { 36 // Constants matching return values from libsuspend 37 public static final int SUSPEND_SUCCESS = 0; 38 public static final int SUSPEND_FAILURE = -1; 39 40 @VisibleForTesting 41 static final String TAG = "SystemPowerControlHelper"; 42 @VisibleForTesting 43 static final String SUSPEND_TYPE_MEM = "mem"; 44 @VisibleForTesting 45 static final String SUSPEND_TYPE_DISK = "disk"; 46 47 private static final String SYSFS_POWER_STATE_CONTROL_FILE = "/sys/power/state"; 48 private static final String SYSFS_LAST_RESUME_REASON_FILE = 49 "/sys/kernel/wakeup_reasons/last_resume_reason"; 50 SystemPowerControlHelper()51 private SystemPowerControlHelper() { 52 } 53 54 /** 55 * Forces system to enter deep sleep (Suspend-to-RAM) 56 * 57 * @return {@code SUSPEND_SUCCESS} in case of success and {@code SUSPEND_FAILURE} 58 * if Suspend-to-RAM fails 59 */ forceDeepSleep()60 public static int forceDeepSleep() { 61 return enterSuspend(SUSPEND_TYPE_MEM); 62 } 63 64 /** 65 * Forces system to enter hibernation (Suspend-to-disk) 66 * 67 * @return {@code SUSPEND_SUCCESS} in case of success and {@code SUSPEND_FAILURE} 68 * if Suspend-to-disk fails 69 */ forceHibernate()70 public static int forceHibernate() { 71 return enterSuspend(SUSPEND_TYPE_DISK); 72 } 73 74 /** 75 * Gets whether the device supports deep sleep 76 */ isSystemSupportingDeepSleep()77 public static boolean isSystemSupportingDeepSleep() { 78 return isSuspendTypeSupported(SUSPEND_TYPE_MEM); 79 } 80 81 /** 82 * Gets whether the device supports hibernation 83 */ isSystemSupportingHibernation()84 public static boolean isSystemSupportingHibernation() { 85 return isSuspendTypeSupported(SUSPEND_TYPE_DISK); 86 } 87 88 /** 89 * Gets the last resume reason at /sys/kernel/wakeup_reasons/last_resume_reason written by the 90 * kernel. 91 */ 92 @NonNull readLastResumeReason()93 public static String readLastResumeReason() { 94 String sysFsLastResumeReasonFile = getSysFsLastResumeReasonFile(); 95 try (BufferedReader reader = 96 new BufferedReader(new FileReader(sysFsLastResumeReasonFile))) { 97 String fileContents = reader.readLine(); 98 if (fileContents == null) { 99 return ""; 100 } else { 101 return fileContents.trim(); 102 } 103 } catch (IOException e) { 104 Slogf.w(TAG, e, "Failed to read %s", sysFsLastResumeReasonFile); 105 return ""; 106 } 107 } 108 109 @VisibleForTesting getSysFsPowerControlFile()110 static String getSysFsPowerControlFile() { 111 return SYSFS_POWER_STATE_CONTROL_FILE; 112 } 113 114 @VisibleForTesting getSysFsLastResumeReasonFile()115 static String getSysFsLastResumeReasonFile() { 116 return SYSFS_LAST_RESUME_REASON_FILE; 117 } 118 119 /* 120 * To match libsuspend API, functions returns SUSPEND_FAILURE(-1) in case of error 121 * and SUSPEND_SUCCESS(0) on Success 122 */ enterSuspend(String mode)123 private static int enterSuspend(String mode) { 124 String sysFsPowerControlFile = getSysFsPowerControlFile(); 125 126 try (BufferedWriter writer = new BufferedWriter(new FileWriter(sysFsPowerControlFile))) { 127 writer.write(mode); 128 writer.flush(); 129 } catch (IOException e) { 130 Slogf.e(TAG, e, "Failed to suspend. Target %s. Failed to write to %s", mode, 131 sysFsPowerControlFile); 132 return SUSPEND_FAILURE; 133 } 134 return SUSPEND_SUCCESS; 135 } 136 isSuspendTypeSupported(@onNull String suspendType)137 private static boolean isSuspendTypeSupported(@NonNull String suspendType) { 138 String sysFsPowerControlFile = getSysFsPowerControlFile(); 139 140 boolean isSuspendTypeSupported = false; 141 try { 142 String fileContents = IoUtils.readFileAsString(sysFsPowerControlFile).trim(); 143 for (String supported : fileContents.split(" ")) { 144 if (suspendType.equals(supported)) { 145 isSuspendTypeSupported = true; 146 break; 147 } 148 } 149 } catch (IOException e) { 150 Slogf.e(TAG, e, "Failed to check supported suspend types. Target %s." 151 + " Unable to read %s", suspendType, sysFsPowerControlFile); 152 } 153 154 return isSuspendTypeSupported; 155 } 156 } 157