1 /*
2  * Copyright (C) 2018 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.garagemode;
18 
19 import static com.android.car.internal.testing.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
20 
21 import android.app.job.JobScheduler;
22 import android.car.hardware.power.CarPowerManager;
23 import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
24 import android.car.hardware.power.CarPowerManager.CarPowerStateListenerWithCompletion;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.os.Handler;
28 import android.os.Looper;
29 import android.os.UserHandle;
30 
31 import com.android.car.CarLocalServices;
32 import com.android.car.CarLog;
33 import com.android.car.internal.testing.ExcludeFromCodeCoverageGeneratedReport;
34 import com.android.car.systeminterface.SystemInterface;
35 import com.android.internal.annotations.VisibleForTesting;
36 import com.android.server.utils.Slogf;
37 
38 import java.io.PrintWriter;
39 import java.util.concurrent.CompletableFuture;
40 
41 /**
42  * Main controller for GarageMode. It controls all the flows of GarageMode and defines the logic.
43  */
44 public class Controller implements CarPowerStateListenerWithCompletion {
45 
46     private static final String TAG = CarLog.tagFor(GarageMode.class) + "_"
47             + Controller.class.getSimpleName();
48 
49     @VisibleForTesting final WakeupPolicy mWakeupPolicy;
50     private final GarageMode mGarageMode;
51     private final Handler mHandler;
52     private final Context mContext;
53     private CarPowerManager mCarPowerManager;
54 
Controller(Context context, Looper looper)55     public Controller(Context context, Looper looper) {
56         this(context, looper, null, null, null);
57     }
58 
Controller( Context context, Looper looper, WakeupPolicy wakeupPolicy, Handler handler, GarageMode garageMode)59     public Controller(
60             Context context,
61             Looper looper,
62             WakeupPolicy wakeupPolicy,
63             Handler handler,
64             GarageMode garageMode) {
65         mContext = context;
66         mHandler = (handler == null ? new Handler(looper) : handler);
67         mWakeupPolicy =
68                 (wakeupPolicy == null ? WakeupPolicy.initFromResources(context) : wakeupPolicy);
69         mGarageMode = (garageMode == null ? new GarageMode(this) : garageMode);
70     }
71 
72     /** init */
init()73     public void init() {
74         mCarPowerManager = CarLocalServices.createCarPowerManager(mContext);
75         mCarPowerManager.setListenerWithCompletion(Controller.this);
76         mGarageMode.init();
77     }
78 
79     /** release */
release()80     public void release() {
81         mCarPowerManager.clearListener();
82         mGarageMode.release();
83     }
84 
85     @Override
onStateChanged(int state, CompletableFuture<Void> future)86     public void onStateChanged(int state, CompletableFuture<Void> future) {
87         switch (state) {
88             case CarPowerStateListener.SHUTDOWN_CANCELLED:
89                 Slogf.d(TAG, "CPM state changed to SHUTDOWN_CANCELLED");
90                 handleShutdownCancelled();
91                 break;
92             case CarPowerStateListener.SHUTDOWN_ENTER:
93                 Slogf.d(TAG, "CPM state changed to SHUTDOWN_ENTER");
94                 handleShutdownEnter();
95                 break;
96             case CarPowerStateListener.SHUTDOWN_PREPARE:
97                 Slogf.d(TAG, "CPM state changed to SHUTDOWN_PREPARE");
98                 handleShutdownPrepare(future);
99                 break;
100             case CarPowerStateListener.SUSPEND_ENTER:
101                 Slogf.d(TAG, "CPM state changed to SUSPEND_ENTER");
102                 handleSuspendEnter();
103                 break;
104             case CarPowerStateListener.SUSPEND_EXIT:
105                 Slogf.d(TAG, "CPM state changed to SUSPEND_EXIT");
106                 handleSuspendExit();
107                 break;
108             default:
109         }
110     }
111 
112     /**
113      * @return boolean whether any jobs are currently in running that GarageMode cares about
114      */
isGarageModeActive()115     boolean isGarageModeActive() {
116         return mGarageMode.isGarageModeActive();
117     }
118 
119     /**
120      * Prints Garage Mode's status, including what jobs it is waiting for
121      */
122     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(PrintWriter writer)123     void dump(PrintWriter writer) {
124         mGarageMode.dump(writer);
125     }
126 
127     /**
128      * Wrapper method to send a broadcast
129      *
130      * @param i intent that contains broadcast data
131      */
sendBroadcast(Intent i)132     void sendBroadcast(Intent i) {
133         SystemInterface systemInterface = CarLocalServices.getService(SystemInterface.class);
134         Slogf.d(TAG, "Sending broadcast with action: %s", i.getAction());
135         systemInterface.sendBroadcastAsUser(i, UserHandle.ALL);
136     }
137 
138     /**
139      * @return JobSchedulerService instance
140      */
getJobSchedulerService()141     JobScheduler getJobSchedulerService() {
142         return (JobScheduler) mContext.getSystemService(Context.JOB_SCHEDULER_SERVICE);
143     }
144 
145     /**
146      * @return Handler instance used by controller
147      */
getHandler()148     Handler getHandler() {
149         return mHandler;
150     }
151 
152     /**
153      * Initiates GarageMode flow which will set the system idleness to true and will start
154      * monitoring jobs which has idleness constraint enabled.
155      */
initiateGarageMode(CompletableFuture<Void> future)156     void initiateGarageMode(CompletableFuture<Void> future) {
157         mWakeupPolicy.incrementCounter();
158         mGarageMode.enterGarageMode(future);
159     }
160 
161     /**
162      * Resets GarageMode.
163      */
resetGarageMode()164     void resetGarageMode() {
165         mGarageMode.cancel();
166         mWakeupPolicy.resetCounter();
167     }
168 
169     @VisibleForTesting
finishGarageMode()170     void finishGarageMode() {
171         mGarageMode.finish();
172     }
173 
174     @VisibleForTesting
setCarPowerManager(CarPowerManager cpm)175     void setCarPowerManager(CarPowerManager cpm) {
176         mCarPowerManager = cpm;
177     }
178 
scheduleNextWakeup()179     void scheduleNextWakeup() {
180         if (mWakeupPolicy.getNextWakeUpInterval() <= 0) {
181             // Either there is no policy or nothing left to schedule
182             return;
183         }
184         int seconds = mWakeupPolicy.getNextWakeUpInterval();
185         mCarPowerManager.scheduleNextWakeupTime(seconds);
186     }
187 
handleSuspendExit()188     private void handleSuspendExit() {
189         resetGarageMode();
190     }
191 
handleSuspendEnter()192     private void handleSuspendEnter() {
193         resetGarageMode();
194     }
195 
handleShutdownEnter()196     private void handleShutdownEnter() {
197         resetGarageMode();
198     }
199 
handleShutdownPrepare(CompletableFuture<Void> future)200     private void handleShutdownPrepare(CompletableFuture<Void> future) {
201         initiateGarageMode(future);
202     }
203 
handleShutdownCancelled()204     private void handleShutdownCancelled() {
205         resetGarageMode();
206     }
207 }
208