• 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.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