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.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
20 
21 import android.annotation.Nullable;
22 import android.car.builtin.util.Slogf;
23 import android.car.hardware.power.CarPowerManager;
24 import android.car.hardware.power.ICarPowerStateListener;
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 import android.util.Log;
31 
32 import com.android.car.CarLocalServices;
33 import com.android.car.CarLog;
34 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
35 import com.android.car.internal.util.IndentingPrintWriter;
36 import com.android.car.power.CarPowerManagementService;
37 import com.android.car.systeminterface.SystemInterface;
38 import com.android.internal.annotations.VisibleForTesting;
39 
40 /**
41  * Main controller for GarageMode. It controls all the flows of GarageMode and defines the logic.
42  */
43 public class GarageModeController extends ICarPowerStateListener.Stub {
44 
45     private static final String TAG = CarLog.tagFor(GarageMode.class) + "_"
46             + GarageModeController.class.getSimpleName();
47     private static final boolean DBG = Slogf.isLoggable(TAG, Log.DEBUG);
48 
49     private final GarageMode mGarageMode;
50     private final Handler mHandler;
51     private CarPowerManagementService mCarPowerService;
52 
GarageModeController(Context context, Looper looper)53     public GarageModeController(Context context, Looper looper) {
54         this(context, looper, /* handler= */ null, /* garageMode= */ null);
55     }
56 
GarageModeController(Context context, Looper looper, Handler handler, GarageMode garageMode)57     public GarageModeController(Context context, Looper looper, Handler handler,
58             GarageMode garageMode) {
59         mHandler = (handler == null) ? new Handler(looper) : handler;
60         mGarageMode = (garageMode == null) ? new GarageMode(context, this) : garageMode;
61     }
62 
63     /** init */
init()64     public void init() {
65         mCarPowerService = CarLocalServices.getService(CarPowerManagementService.class);
66         mCarPowerService.registerInternalListener(GarageModeController.this);
67         mGarageMode.init();
68         Slogf.e("GarageMode", "init");
69     }
70 
71     /** release */
release()72     public void release() {
73         mCarPowerService.unregisterInternalListener(GarageModeController.this);
74         mGarageMode.release();
75     }
76 
77     // This function runs in the CarPowerManagementService handler thread, which is a different
78     // thread than mHandler is using.
79     @Override
onStateChanged(int state, long expirationTimeMs)80     public void onStateChanged(int state, long expirationTimeMs) {
81         if (DBG) {
82             Slogf.d(TAG, "CPM state changed to %s",
83                     CarPowerManagementService.powerStateToString(state));
84         }
85         switch (state) {
86             case CarPowerManager.STATE_SHUTDOWN_CANCELLED:
87                 resetGarageMode(null);
88                 break;
89             case CarPowerManager.STATE_SHUTDOWN_ENTER:
90             case CarPowerManager.STATE_SUSPEND_ENTER:
91             case CarPowerManager.STATE_HIBERNATION_ENTER:
92                 resetGarageMode(() -> {
93                     mCarPowerService.completeHandlingPowerStateChange(state,
94                             GarageModeController.this);
95                 });
96                 break;
97             case CarPowerManager.STATE_PRE_SHUTDOWN_PREPARE:
98             case CarPowerManager.STATE_POST_SHUTDOWN_ENTER:
99             case CarPowerManager.STATE_POST_SUSPEND_ENTER:
100             case CarPowerManager.STATE_POST_HIBERNATION_ENTER:
101                 mCarPowerService.completeHandlingPowerStateChange(state, GarageModeController.this);
102                 break;
103             case CarPowerManager.STATE_SHUTDOWN_PREPARE:
104                 initiateGarageMode(
105                         () -> mCarPowerService.completeHandlingPowerStateChange(state,
106                                 GarageModeController.this));
107                 break;
108             default:
109                 break;
110         }
111     }
112 
113     /**
114      * @return boolean whether any jobs are currently in running that GarageMode cares about
115      */
isGarageModeActive()116     boolean isGarageModeActive() {
117         return mGarageMode.isGarageModeActive();
118     }
119 
120     /**
121      * Prints Garage Mode's status, including what jobs it is waiting for
122      */
123     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(IndentingPrintWriter writer)124     void dump(IndentingPrintWriter writer) {
125         mGarageMode.dump(writer);
126     }
127 
128     /**
129      * Wrapper method to send a broadcast
130      *
131      * @param i intent that contains broadcast data
132      */
sendBroadcast(Intent i)133     void sendBroadcast(Intent i) {
134         SystemInterface systemInterface = CarLocalServices.getService(SystemInterface.class);
135         if (DBG) {
136             Slogf.d(TAG, "Sending broadcast with action: %s", i.getAction());
137         }
138         systemInterface.sendBroadcastAsUser(i, UserHandle.ALL);
139     }
140 
141     /**
142      * @return Handler instance used by controller
143      */
getHandler()144     Handler getHandler() {
145         return mHandler;
146     }
147 
148     /**
149      * Initiates GarageMode flow which will set the system idleness to true and will start
150      * monitoring jobs which has idleness constraint enabled.
151      */
initiateGarageMode(Runnable completor)152     void initiateGarageMode(Runnable completor) {
153         mHandler.post(() -> mGarageMode.enterGarageMode(completor));
154     }
155 
156     /**
157      * Resets GarageMode.
158      */
resetGarageMode(@ullable Runnable completor)159     void resetGarageMode(@Nullable Runnable completor) {
160         mHandler.post(() -> mGarageMode.cancel(completor));
161     }
162 
163     @VisibleForTesting
finishGarageMode()164     void finishGarageMode() {
165         mHandler.post(() -> mGarageMode.finish());
166     }
167 }
168