1 /* 2 * Copyright (C) 2016 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.content.BroadcastReceiver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.IntentFilter; 23 import android.os.Looper; 24 import android.os.PowerManager; 25 import android.util.Log; 26 import android.util.Pair; 27 28 import com.android.car.procfsinspector.ProcessInfo; 29 import com.android.car.procfsinspector.ProcfsInspector; 30 import com.android.internal.annotations.VisibleForTesting; 31 import com.android.internal.car.ICarServiceHelper; 32 33 import java.time.Duration; 34 import java.util.ArrayList; 35 import java.util.List; 36 import java.util.concurrent.CountDownLatch; 37 import java.util.concurrent.Executors; 38 import java.util.concurrent.ScheduledExecutorService; 39 import java.util.concurrent.TimeUnit; 40 41 /** 42 * Interface that abstracts system status (booted, sleeping, ...) operations 43 */ 44 public interface SystemStateInterface { 45 static final String TAG = SystemStateInterface.class.getSimpleName(); shutdown()46 void shutdown(); 47 /** 48 * Put the device into Suspend to RAM mode 49 * @return boolean true if suspend succeeded 50 */ enterDeepSleep()51 boolean enterDeepSleep(); scheduleActionForBootCompleted(Runnable action, Duration delay)52 void scheduleActionForBootCompleted(Runnable action, Duration delay); 53 isWakeupCausedByTimer()54 default boolean isWakeupCausedByTimer() { 55 //TODO bug: 32061842, check wake up reason and do necessary operation information should 56 // come from kernel. it can be either power on or wake up for maintenance 57 // power on will involve GPIO trigger from power controller 58 // its own wakeup will involve timer expiration. 59 return false; 60 } 61 isSystemSupportingDeepSleep()62 default boolean isSystemSupportingDeepSleep() { 63 //TODO should return by checking some kernel suspend control sysfs, bug: 32061842 64 return true; 65 } 66 getRunningProcesses()67 default List<ProcessInfo> getRunningProcesses() { 68 return ProcfsInspector.readProcessTable(); 69 } 70 setCarServiceHelper(ICarServiceHelper helper)71 default void setCarServiceHelper(ICarServiceHelper helper) { 72 // Do nothing 73 } 74 75 /** 76 * Default implementation that is used internally. 77 */ 78 @VisibleForTesting 79 class DefaultImpl implements SystemStateInterface { 80 private static final int MAX_WAIT_FOR_HELPER_SEC = 10; 81 private static final Duration MIN_BOOT_COMPLETE_ACTION_DELAY = Duration.ofSeconds(10); 82 private static final int SUSPEND_TRY_TIMEOUT_MS = 1_000; 83 84 private ICarServiceHelper mICarServiceHelper; // mHelperLatch becomes 0 when this is set 85 private final CountDownLatch mHelperLatch = new CountDownLatch(1); 86 private final Context mContext; 87 private final PowerManager mPowerManager; 88 private List<Pair<Runnable, Duration>> mActionsList = new ArrayList<>(); 89 private ScheduledExecutorService mExecutorService; 90 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 91 @Override 92 public void onReceive(Context context, Intent intent) { 93 if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { 94 for (Pair<Runnable, Duration> action : mActionsList) { 95 mExecutorService.schedule(action.first, 96 action.second.toMillis(), TimeUnit.MILLISECONDS); 97 } 98 } 99 } 100 }; 101 102 @VisibleForTesting DefaultImpl(Context context)103 public DefaultImpl(Context context) { 104 mContext = context; 105 mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 106 } 107 108 @Override shutdown()109 public void shutdown() { 110 mPowerManager.shutdown(false /* no confirm*/, null, true /* true */); 111 } 112 113 @Override enterDeepSleep()114 public boolean enterDeepSleep() { 115 // TODO(b/32061842) Set wake up time via VHAL 116 if (!canInvokeHelper()) { 117 return false; 118 } 119 120 boolean deviceEnteredSleep = false; 121 try { 122 int retVal = mICarServiceHelper.forceSuspend(SUSPEND_TRY_TIMEOUT_MS); 123 deviceEnteredSleep = retVal == 0; 124 } catch (Exception e) { 125 Log.e(TAG, "Unable to enter deep sleep", e); 126 } 127 return deviceEnteredSleep; 128 } 129 130 // Checks if mICarServiceHelper is available. (It might be unavailable if 131 // we are asked to shut down before we're completely up and running.) 132 // If the helper is null, wait for it to be set. 133 // Returns true if the helper is available. canInvokeHelper()134 private boolean canInvokeHelper() { 135 if (Looper.myLooper() == Looper.getMainLooper()) { 136 // We should not be called from the main thread! 137 throw new IllegalStateException("SystemStateInterface.enterDeepSleep() " 138 + "was called from the main thread"); 139 } 140 if (mICarServiceHelper != null) { 141 return true; 142 } 143 // We have no helper. If we wait, maybe we will get a helper. 144 try { 145 mHelperLatch.await(MAX_WAIT_FOR_HELPER_SEC, TimeUnit.SECONDS); 146 } catch (InterruptedException ie) { 147 Thread.currentThread().interrupt(); // Restore interrupted status 148 } 149 if (mICarServiceHelper != null) { 150 return true; 151 } 152 Log.e(TAG, "Unable to enter deep sleep: ICarServiceHelper is still null " 153 + "after waiting " + MAX_WAIT_FOR_HELPER_SEC + " seconds"); 154 return false; 155 } 156 157 @Override scheduleActionForBootCompleted(Runnable action, Duration delay)158 public void scheduleActionForBootCompleted(Runnable action, Duration delay) { 159 if (MIN_BOOT_COMPLETE_ACTION_DELAY.compareTo(delay) < 0) { 160 // TODO: consider adding some degree of randomness here 161 delay = MIN_BOOT_COMPLETE_ACTION_DELAY; 162 } 163 if (mActionsList.isEmpty()) { 164 final int corePoolSize = 1; 165 mExecutorService = Executors.newScheduledThreadPool(corePoolSize); 166 IntentFilter intentFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); 167 mContext.registerReceiver(mBroadcastReceiver, intentFilter); 168 } 169 mActionsList.add(Pair.create(action, delay)); 170 } 171 172 @Override setCarServiceHelper(ICarServiceHelper helper)173 public void setCarServiceHelper(ICarServiceHelper helper) { 174 mICarServiceHelper = helper; 175 mHelperLatch.countDown(); 176 } 177 } 178 } 179