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.BufferedWriter;
27 import java.io.FileWriter;
28 import java.io.IOException;
29 
30 /**
31  * Uses sysfs(sys/power/state) to force device into suspend
32  */
33 public final class SystemPowerControlHelper {
34     // Constants matching return values from libsuspend
35     public static final int SUSPEND_RESULT_SUCCESS = 0;
36     public static final int SUSPEND_RESULT_FAILURE = -1;
37 
38     @VisibleForTesting
39     static final String TAG = "SystemPowerControlHelper";
40     @VisibleForTesting
41     static final String SUSPEND_TYPE_MEM = "mem";
42     @VisibleForTesting
43     static final String SUSPEND_TYPE_DISK = "disk";
44 
45     private static final String SYSFS_POWER_STATE_CONTROL_FILE = "/sys/power/state";
46 
SystemPowerControlHelper()47     private SystemPowerControlHelper() {
48     }
49 
50     /**
51      * Forces system to enter deep sleep (Suspend-to-RAM)
52      *
53      * @return {@code SUSPEND_RESULT_SUCCESS} in case of success and {@code SUSPEND_RESULT_FAILURE}
54      * if Suspend-to-RAM fails
55      */
forceDeepSleep()56     public static int forceDeepSleep() {
57         return enterSuspend(SUSPEND_TYPE_MEM);
58     }
59 
60     /**
61      * Forces system to enter hibernation (Suspend-to-disk)
62      *
63      * @return {@code SUSPEND_RESULT_SUCCESS} in case of success and {@code SUSPEND_RESULT_FAILURE}
64      * if Suspend-to-disk fails
65      */
forceHibernate()66     public static int forceHibernate() {
67         return enterSuspend(SUSPEND_TYPE_DISK);
68     }
69 
70     /**
71      * Gets whether the device supports deep sleep
72      */
isSystemSupportingDeepSleep()73     public static boolean isSystemSupportingDeepSleep() {
74         return isSuspendTypeSupported(SUSPEND_TYPE_MEM);
75     }
76 
77     /**
78      * Gets whether the device supports hibernation
79      */
isSystemSupportingHibernation()80     public static boolean isSystemSupportingHibernation() {
81         return isSuspendTypeSupported(SUSPEND_TYPE_DISK);
82     }
83 
84 
85     @VisibleForTesting
getSysFsPowerControlFile()86     static String getSysFsPowerControlFile() {
87         return SYSFS_POWER_STATE_CONTROL_FILE;
88     }
89 
90     /*
91      * To match libsuspend API, functions returns SUSPEND_FAILURE(-1) in case of error
92      * and SUSPEND_SUCCESS(0) on Success
93      */
enterSuspend(String mode)94     private static int enterSuspend(String mode) {
95         String sysFsPowerControlFile = getSysFsPowerControlFile();
96 
97         try (BufferedWriter writer = new BufferedWriter(new FileWriter(sysFsPowerControlFile))) {
98             writer.write(mode);
99             writer.flush();
100         } catch (IOException e) {
101             Slogf.e(TAG, e, "Failed to suspend. Target %s. Failed to write to %s", mode,
102                     sysFsPowerControlFile);
103             return SUSPEND_RESULT_FAILURE;
104         }
105         return SUSPEND_RESULT_SUCCESS;
106     }
107 
isSuspendTypeSupported(@onNull String suspendType)108     private static boolean isSuspendTypeSupported(@NonNull String suspendType) {
109         String sysFsPowerControlFile = getSysFsPowerControlFile();
110 
111         boolean isSuspendTypeSupported = false;
112         try {
113             String fileContents = IoUtils.readFileAsString(sysFsPowerControlFile).trim();
114             for (String supported : fileContents.split(" ")) {
115                 if (suspendType.equals(supported)) {
116                     isSuspendTypeSupported = true;
117                     break;
118                 }
119             }
120         } catch (IOException e) {
121             Slogf.e(TAG, e, "Failed to check supported suspend types. Target %s."
122                     + " Unable to read %s", suspendType, sysFsPowerControlFile);
123         }
124 
125         return isSuspendTypeSupported;
126     }
127 }
128