blob: 041ba299620f840a725c7b80e070ae127339effc [file] [log] [blame]
Eric Jeong38ae8212020-01-14 10:25:10 -08001/*
2 * Copyright (C) 2020 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
17package com.android.car.watchdog;
18
Eric Jeong01a28aa2020-03-27 16:43:19 -070019import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STARTING;
20import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STOPPED;
Eric Jeong09657562020-03-20 16:02:39 -070021import static android.car.watchdog.CarWatchdogManager.TIMEOUT_CRITICAL;
22import static android.car.watchdog.CarWatchdogManager.TIMEOUT_MODERATE;
23import static android.car.watchdog.CarWatchdogManager.TIMEOUT_NORMAL;
Eric Jeongae2c04c2020-02-21 09:18:31 -080024
Eric Jeong09657562020-03-20 16:02:39 -070025import static com.android.car.CarLog.TAG_WATCHDOG;
26import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
27
28import android.annotation.NonNull;
Eric Jeong01a28aa2020-03-27 16:43:19 -070029import android.annotation.UserIdInt;
Eric Jeong38ae8212020-01-14 10:25:10 -080030import android.automotive.watchdog.ICarWatchdogClient;
Eric Jeong27a178f2020-03-24 16:39:39 -070031import android.automotive.watchdog.PowerCycle;
32import android.automotive.watchdog.StateType;
Eric Jeong01a28aa2020-03-27 16:43:19 -070033import android.automotive.watchdog.UserState;
Eric Jeong27a178f2020-03-24 16:39:39 -070034import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
35import android.car.hardware.power.ICarPowerStateListener;
Eric Jeong38ae8212020-01-14 10:25:10 -080036import android.car.watchdog.ICarWatchdogService;
Eric Jeong6df075f2020-03-11 16:12:23 -070037import android.car.watchdoglib.CarWatchdogDaemonHelper;
Eric Jeongae2c04c2020-02-21 09:18:31 -080038import android.content.Context;
Eric Jeong01a28aa2020-03-27 16:43:19 -070039import android.content.pm.UserInfo;
Eric Jeong09657562020-03-20 16:02:39 -070040import android.os.Binder;
Eric Jeongae2c04c2020-02-21 09:18:31 -080041import android.os.Handler;
Eric Jeong09657562020-03-20 16:02:39 -070042import android.os.IBinder;
Eric Jeongae2c04c2020-02-21 09:18:31 -080043import android.os.Looper;
44import android.os.RemoteException;
Eric Jeong01a28aa2020-03-27 16:43:19 -070045import android.os.UserHandle;
46import android.os.UserManager;
Eric Jeongae2c04c2020-02-21 09:18:31 -080047import android.util.Log;
Eric Jeong09657562020-03-20 16:02:39 -070048import android.util.SparseArray;
Eric Jeong01a28aa2020-03-27 16:43:19 -070049import android.util.SparseBooleanArray;
Eric Jeongae2c04c2020-02-21 09:18:31 -080050
Eric Jeong27a178f2020-03-24 16:39:39 -070051import com.android.car.CarLocalServices;
52import com.android.car.CarPowerManagementService;
Eric Jeong38ae8212020-01-14 10:25:10 -080053import com.android.car.CarServiceBase;
Eric Jeong01a28aa2020-03-27 16:43:19 -070054import com.android.car.user.CarUserService;
Eric Jeong09657562020-03-20 16:02:39 -070055import com.android.internal.annotations.GuardedBy;
Eric Jeong0ee72812020-04-21 15:23:54 -070056import com.android.internal.annotations.VisibleForTesting;
Eric Jeong38ae8212020-01-14 10:25:10 -080057
58import java.io.PrintWriter;
59import java.lang.ref.WeakReference;
Eric Jeong09657562020-03-20 16:02:39 -070060import java.util.ArrayList;
Eric Jeong01a28aa2020-03-27 16:43:19 -070061import java.util.List;
Eric Jeong38ae8212020-01-14 10:25:10 -080062
63/**
64 * Service to implement CarWatchdogManager API.
65 *
66 * <p>CarWatchdogService runs as car watchdog mediator, which checks clients' health status and
67 * reports the result to car watchdog server.
68 */
69public final class CarWatchdogService extends ICarWatchdogService.Stub implements CarServiceBase {
70
Eric Jeong09657562020-03-20 16:02:39 -070071 private static final boolean DEBUG = true; // STOPSHIP if true (b/151474489)
72 private static final String TAG = TAG_WATCHDOG;
73 private static final int[] ALL_TIMEOUTS =
74 { TIMEOUT_CRITICAL, TIMEOUT_MODERATE, TIMEOUT_NORMAL };
75
Eric Jeongae2c04c2020-02-21 09:18:31 -080076 private final Context mContext;
77 private final ICarWatchdogClientImpl mWatchdogClient;
Eric Jeongae2c04c2020-02-21 09:18:31 -080078 private final Handler mMainHandler = new Handler(Looper.getMainLooper());
Eric Jeong09657562020-03-20 16:02:39 -070079 private final CarWatchdogDaemonHelper mCarWatchdogDaemonHelper;
Eric Jeong6df075f2020-03-11 16:12:23 -070080 private final CarWatchdogDaemonHelper.OnConnectionChangeListener mConnectionListener =
81 (connected) -> {
82 if (connected) {
83 registerToDaemon();
84 }
85 };
Eric Jeong09657562020-03-20 16:02:39 -070086 private final Object mLock = new Object();
87 /*
88 * Keeps the list of car watchdog client according to timeout:
89 * key => timeout, value => ClientInfo list.
90 * The value of SparseArray is guarded by mLock.
91 */
92 @GuardedBy("mLock")
93 private final SparseArray<ArrayList<ClientInfo>> mClientMap = new SparseArray<>();
94 /*
95 * Keeps the map of car watchdog client being checked by CarWatchdogService according to
96 * timeout: key => timeout, value => ClientInfo map.
97 * The value is also a map: key => session id, value => ClientInfo.
98 */
99 @GuardedBy("mLock")
100 private final SparseArray<SparseArray<ClientInfo>> mPingedClientMap = new SparseArray<>();
101 /*
102 * Keeps whether client health checking is being performed according to timeout:
103 * key => timeout, value => boolean (whether client health checking is being performed).
104 * The value of SparseArray is guarded by mLock.
105 */
106 @GuardedBy("mLock")
107 private final SparseArray<Boolean> mClientCheckInProgress = new SparseArray<>();
108 @GuardedBy("mLock")
Eric Jeong515009b2020-04-10 17:23:56 -0700109 private final ArrayList<ClientInfo> mClientsNotResponding = new ArrayList<>();
Eric Jeong09657562020-03-20 16:02:39 -0700110 @GuardedBy("mMainHandler")
111 private int mLastSessionId;
Eric Jeong01a28aa2020-03-27 16:43:19 -0700112 @GuardedBy("mMainHandler")
113 private final SparseBooleanArray mStoppedUser = new SparseBooleanArray();
Eric Jeong09657562020-03-20 16:02:39 -0700114
Eric Jeongae2c04c2020-02-21 09:18:31 -0800115 @VisibleForTesting
Eric Jeong0ee72812020-04-21 15:23:54 -0700116 public CarWatchdogService(Context context) {
Eric Jeongae2c04c2020-02-21 09:18:31 -0800117 mContext = context;
Eric Jeong0ee72812020-04-21 15:23:54 -0700118 mCarWatchdogDaemonHelper = new CarWatchdogDaemonHelper(TAG_WATCHDOG);
Eric Jeongae2c04c2020-02-21 09:18:31 -0800119 mWatchdogClient = new ICarWatchdogClientImpl(this);
Eric Jeong38ae8212020-01-14 10:25:10 -0800120 }
121
122 @Override
123 public void init() {
Eric Jeong09657562020-03-20 16:02:39 -0700124 for (int timeout : ALL_TIMEOUTS) {
125 mClientMap.put(timeout, new ArrayList<ClientInfo>());
126 mPingedClientMap.put(timeout, new SparseArray<ClientInfo>());
127 mClientCheckInProgress.put(timeout, false);
128 }
Eric Jeong01a28aa2020-03-27 16:43:19 -0700129 subscribePowerCycleChange();
130 subscribeUserStateChange();
Eric Jeong6df075f2020-03-11 16:12:23 -0700131 mCarWatchdogDaemonHelper.addOnConnectionChangeListener(mConnectionListener);
132 mCarWatchdogDaemonHelper.connect();
Eric Jeong09657562020-03-20 16:02:39 -0700133 if (DEBUG) {
134 Log.d(TAG, "CarWatchdogService is initialized");
135 }
Eric Jeong38ae8212020-01-14 10:25:10 -0800136 }
137
138 @Override
139 public void release() {
Eric Jeong6df075f2020-03-11 16:12:23 -0700140 unregisterFromDaemon();
141 mCarWatchdogDaemonHelper.disconnect();
Eric Jeong38ae8212020-01-14 10:25:10 -0800142 }
143
144 @Override
145 public void dump(PrintWriter writer) {
Eric Jeong09657562020-03-20 16:02:39 -0700146 String indent = " ";
147 int count = 1;
Eric Jeong09657562020-03-20 16:02:39 -0700148 synchronized (mLock) {
Eric Jeong01a28aa2020-03-27 16:43:19 -0700149 writer.println("*CarWatchdogService*");
150 writer.println("Registered clients");
Eric Jeong09657562020-03-20 16:02:39 -0700151 for (int timeout : ALL_TIMEOUTS) {
152 ArrayList<ClientInfo> clients = mClientMap.get(timeout);
153 String timeoutStr = timeoutToString(timeout);
154 for (int i = 0; i < clients.size(); i++, count++) {
155 ClientInfo clientInfo = clients.get(i);
156 writer.printf("%sclient #%d: timeout = %s, pid = %d\n",
157 indent, count, timeoutStr, clientInfo.pid);
158 }
159 }
Eric Jeong01a28aa2020-03-27 16:43:19 -0700160 writer.printf("Stopped users: ");
161 int size = mStoppedUser.size();
162 if (size > 0) {
163 writer.printf("%d", mStoppedUser.keyAt(0));
164 for (int i = 1; i < size; i++) {
165 writer.printf(", %d", mStoppedUser.keyAt(i));
166 }
167 writer.printf("\n");
168 } else {
169 writer.printf("none\n");
170 }
Eric Jeong09657562020-03-20 16:02:39 -0700171 }
Eric Jeong38ae8212020-01-14 10:25:10 -0800172 }
173
Eric Jeong09657562020-03-20 16:02:39 -0700174 /**
175 * Registers {@link android.automotive.watchdog. ICarWatchdogClient} to
176 * {@link CarWatchdogService}.
177 */
Eric Jeong38ae8212020-01-14 10:25:10 -0800178 @Override
179 public void registerClient(ICarWatchdogClient client, int timeout) {
Eric Jeong09657562020-03-20 16:02:39 -0700180 ArrayList<ClientInfo> clients = mClientMap.get(timeout);
181 if (clients == null) {
182 Log.w(TAG, "Cannot register the client: invalid timeout");
183 return;
184 }
185 synchronized (mLock) {
186 IBinder binder = client.asBinder();
187 for (int i = 0; i < clients.size(); i++) {
188 ClientInfo clientInfo = clients.get(i);
189 if (binder == clientInfo.client.asBinder()) {
190 Log.w(TAG,
191 "Cannot register the client: the client(pid:" + clientInfo.pid
192 + ") has been already registered");
193 return;
194 }
195 }
196 int pid = Binder.getCallingPid();
Eric Jeong01a28aa2020-03-27 16:43:19 -0700197 int userId = UserHandle.getUserId(Binder.getCallingUid());
198 ClientInfo clientInfo = new ClientInfo(client, pid, userId, timeout);
Eric Jeong09657562020-03-20 16:02:39 -0700199 try {
200 clientInfo.linkToDeath();
201 } catch (RemoteException e) {
202 Log.w(TAG, "Cannot register the client: linkToDeath to the client failed");
203 return;
204 }
205 clients.add(clientInfo);
206 if (DEBUG) {
207 Log.d(TAG, "Client(pid: " + pid + ") is registered");
208 }
209 }
Eric Jeong38ae8212020-01-14 10:25:10 -0800210 }
211
Eric Jeong09657562020-03-20 16:02:39 -0700212 /**
213 * Unregisters {@link android.automotive.watchdog. ICarWatchdogClient} from
214 * {@link CarWatchdogService}.
215 */
Eric Jeong38ae8212020-01-14 10:25:10 -0800216 @Override
217 public void unregisterClient(ICarWatchdogClient client) {
Eric Jeong09657562020-03-20 16:02:39 -0700218 synchronized (mLock) {
219 IBinder binder = client.asBinder();
220 for (int timeout : ALL_TIMEOUTS) {
221 ArrayList<ClientInfo> clients = mClientMap.get(timeout);
222 for (int i = 0; i < clients.size(); i++) {
223 ClientInfo clientInfo = clients.get(i);
224 if (binder != clientInfo.client.asBinder()) {
225 continue;
226 }
227 clientInfo.unlinkToDeath();
228 clients.remove(i);
229 if (DEBUG) {
230 Log.d(TAG, "Client(pid: " + clientInfo.pid + ") is unregistered");
231 }
232 return;
233 }
234 }
235 }
236 Log.w(TAG, "Cannot unregister the client: the client has not been registered before");
237 return;
Eric Jeong38ae8212020-01-14 10:25:10 -0800238 }
239
Eric Jeong09657562020-03-20 16:02:39 -0700240 /**
241 * Tells {@link CarWatchdogService} that the client is alive.
242 */
Eric Jeong38ae8212020-01-14 10:25:10 -0800243 @Override
244 public void tellClientAlive(ICarWatchdogClient client, int sessionId) {
Eric Jeong09657562020-03-20 16:02:39 -0700245 synchronized (mLock) {
246 for (int timeout : ALL_TIMEOUTS) {
247 if (!mClientCheckInProgress.get(timeout)) {
248 continue;
249 }
250 SparseArray<ClientInfo> pingedClients = mPingedClientMap.get(timeout);
251 ClientInfo clientInfo = pingedClients.get(sessionId);
252 if (clientInfo != null && clientInfo.client.asBinder() == client.asBinder()) {
253 pingedClients.remove(sessionId);
254 return;
255 }
256 }
257 }
Eric Jeong38ae8212020-01-14 10:25:10 -0800258 }
259
Eric Jeong0ee72812020-04-21 15:23:54 -0700260 @VisibleForTesting
261 protected int getClientCount(int timeout) {
262 synchronized (mLock) {
263 ArrayList<ClientInfo> clients = mClientMap.get(timeout);
264 return clients != null ? clients.size() : 0;
265 }
266 }
267
Eric Jeong6df075f2020-03-11 16:12:23 -0700268 private void registerToDaemon() {
Eric Jeongae2c04c2020-02-21 09:18:31 -0800269 try {
Eric Jeong6df075f2020-03-11 16:12:23 -0700270 mCarWatchdogDaemonHelper.registerMediator(mWatchdogClient);
Eric Jeong09657562020-03-20 16:02:39 -0700271 if (DEBUG) {
272 Log.d(TAG, "CarWatchdogService registers to car watchdog daemon");
273 }
Eric Jeong4825ca32020-04-13 18:07:44 -0700274 } catch (RemoteException | RuntimeException e) {
Eric Jeong09657562020-03-20 16:02:39 -0700275 Log.w(TAG, "Cannot register to car watchdog daemon: " + e);
Eric Jeong6df075f2020-03-11 16:12:23 -0700276 }
Eric Jeong01a28aa2020-03-27 16:43:19 -0700277 UserManager userManager = UserManager.get(mContext);
278 List<UserInfo> users = userManager.getUsers();
279 try {
280 // TODO(b/152780162): reduce the number of RPC calls(isUserRunning).
281 for (UserInfo info : users) {
282 int userState = userManager.isUserRunning(info.id)
283 ? UserState.USER_STATE_STARTED : UserState.USER_STATE_STOPPED;
284 mCarWatchdogDaemonHelper.notifySystemStateChange(StateType.USER_STATE, info.id,
285 userState);
286 if (userState == UserState.USER_STATE_STOPPED) {
287 mStoppedUser.put(info.id, true);
288 } else {
289 mStoppedUser.delete(info.id);
290 }
291 }
Eric Jeong4825ca32020-04-13 18:07:44 -0700292 } catch (RemoteException | RuntimeException e) {
Eric Jeong01a28aa2020-03-27 16:43:19 -0700293 Log.w(TAG, "Notifying system state change failed: " + e);
294 }
Eric Jeong6df075f2020-03-11 16:12:23 -0700295 }
296
297 private void unregisterFromDaemon() {
298 try {
299 mCarWatchdogDaemonHelper.unregisterMediator(mWatchdogClient);
Eric Jeong09657562020-03-20 16:02:39 -0700300 if (DEBUG) {
301 Log.d(TAG, "CarWatchdogService unregisters from car watchdog daemon");
302 }
Eric Jeong4825ca32020-04-13 18:07:44 -0700303 } catch (RemoteException | RuntimeException e) {
Eric Jeong09657562020-03-20 16:02:39 -0700304 Log.w(TAG, "Cannot unregister from car watchdog daemon: " + e);
305 }
306 }
307
308 private void onClientDeath(ICarWatchdogClient client, int timeout) {
309 synchronized (mLock) {
310 removeClientLocked(client.asBinder(), timeout);
Eric Jeongae2c04c2020-02-21 09:18:31 -0800311 }
Eric Jeongae2c04c2020-02-21 09:18:31 -0800312 }
313
Eric Jeong0ee72812020-04-21 15:23:54 -0700314 private void postHealthCheckMessage(int sessionId) {
315 mMainHandler.sendMessage(obtainMessage(CarWatchdogService::doHealthCheck, this, sessionId));
316 }
317
Eric Jeongae2c04c2020-02-21 09:18:31 -0800318 private void doHealthCheck(int sessionId) {
Eric Jeong09657562020-03-20 16:02:39 -0700319 // For critical clients, the response status are checked just before reporting to car
320 // watchdog daemon. For moderate and normal clients, the status are checked after allowed
321 // delay per timeout.
322 analyzeClientResponse(TIMEOUT_CRITICAL);
323 reportHealthCheckResult(sessionId);
324 sendPingToClients(TIMEOUT_CRITICAL);
325 sendPingToClientsAndCheck(TIMEOUT_MODERATE);
326 sendPingToClientsAndCheck(TIMEOUT_NORMAL);
Eric Jeongae2c04c2020-02-21 09:18:31 -0800327 }
328
Eric Jeong09657562020-03-20 16:02:39 -0700329 private void analyzeClientResponse(int timeout) {
330 // Clients which are not responding are stored in mClientsNotResponding, and will be dumped
331 // and killed at the next response of CarWatchdogService to car watchdog daemon.
332 SparseArray<ClientInfo> pingedClients = mPingedClientMap.get(timeout);
333 synchronized (mLock) {
334 for (int i = 0; i < pingedClients.size(); i++) {
335 ClientInfo clientInfo = pingedClients.valueAt(i);
Eric Jeong01a28aa2020-03-27 16:43:19 -0700336 if (mStoppedUser.get(clientInfo.userId)) {
337 continue;
338 }
Eric Jeong515009b2020-04-10 17:23:56 -0700339 mClientsNotResponding.add(clientInfo);
340 removeClientLocked(clientInfo.client.asBinder(), timeout);
Eric Jeong09657562020-03-20 16:02:39 -0700341 }
342 mClientCheckInProgress.setValueAt(timeout, false);
343 }
344 }
345
346 private void sendPingToClients(int timeout) {
347 SparseArray<ClientInfo> pingedClients = mPingedClientMap.get(timeout);
348 ArrayList<ClientInfo> clientsToCheck;
349 synchronized (mLock) {
350 pingedClients.clear();
351 clientsToCheck = new ArrayList<>(mClientMap.get(timeout));
352 for (int i = 0; i < clientsToCheck.size(); i++) {
Eric Jeong09657562020-03-20 16:02:39 -0700353 ClientInfo clientInfo = clientsToCheck.get(i);
Eric Jeong01a28aa2020-03-27 16:43:19 -0700354 if (mStoppedUser.get(clientInfo.userId)) {
355 continue;
356 }
357 int sessionId = getNewSessionId();
Eric Jeong09657562020-03-20 16:02:39 -0700358 clientInfo.sessionId = sessionId;
359 pingedClients.put(sessionId, clientInfo);
360 }
361 mClientCheckInProgress.setValueAt(timeout, true);
362 }
363 for (int i = 0; i < clientsToCheck.size(); i++) {
364 ClientInfo clientInfo = clientsToCheck.get(i);
365 try {
366 clientInfo.client.checkIfAlive(clientInfo.sessionId, timeout);
367 } catch (RemoteException e) {
368 Log.w(TAG, "Sending a ping message to client(pid: " + clientInfo.pid
369 + ") failed: " + e);
370 synchronized (mLock) {
371 pingedClients.remove(clientInfo.sessionId);
372 }
373 }
374 }
375 }
376
377 private void sendPingToClientsAndCheck(int timeout) {
378 synchronized (mLock) {
379 if (mClientCheckInProgress.get(timeout)) {
380 return;
381 }
382 }
383 sendPingToClients(timeout);
384 mMainHandler.sendMessageDelayed(obtainMessage(CarWatchdogService::analyzeClientResponse,
385 this, timeout), timeoutToDurationMs(timeout));
386 }
387
388 private int getNewSessionId() {
389 if (++mLastSessionId <= 0) {
390 mLastSessionId = 1;
391 }
392 return mLastSessionId;
393 }
394
Eric Jeongdeb159f2020-03-23 17:20:05 -0700395 private void removeClientLocked(IBinder clientBinder, int timeout) {
Eric Jeong09657562020-03-20 16:02:39 -0700396 ArrayList<ClientInfo> clients = mClientMap.get(timeout);
397 for (int i = 0; i < clients.size(); i++) {
398 ClientInfo clientInfo = clients.get(i);
399 if (clientBinder == clientInfo.client.asBinder()) {
400 clients.remove(i);
Eric Jeongdeb159f2020-03-23 17:20:05 -0700401 return;
Eric Jeong09657562020-03-20 16:02:39 -0700402 }
403 }
Eric Jeong09657562020-03-20 16:02:39 -0700404 }
405
406 private void reportHealthCheckResult(int sessionId) {
407 int[] clientsNotResponding;
Eric Jeong515009b2020-04-10 17:23:56 -0700408 ArrayList<ClientInfo> clientsToNotify;
Eric Jeong09657562020-03-20 16:02:39 -0700409 synchronized (mLock) {
410 clientsNotResponding = toIntArray(mClientsNotResponding);
Eric Jeong515009b2020-04-10 17:23:56 -0700411 clientsToNotify = new ArrayList<>(mClientsNotResponding);
Eric Jeong09657562020-03-20 16:02:39 -0700412 mClientsNotResponding.clear();
413 }
Eric Jeong515009b2020-04-10 17:23:56 -0700414 for (int i = 0; i < clientsToNotify.size(); i++) {
415 ClientInfo clientInfo = clientsToNotify.get(i);
416 try {
417 clientInfo.client.prepareProcessTermination();
418 } catch (RemoteException e) {
419 Log.w(TAG, "Notifying prepareProcessTermination to client(pid: " + clientInfo.pid
420 + ") failed: " + e);
421 }
422 }
423
Eric Jeong09657562020-03-20 16:02:39 -0700424 try {
425 mCarWatchdogDaemonHelper.tellMediatorAlive(mWatchdogClient, clientsNotResponding,
426 sessionId);
Eric Jeong4825ca32020-04-13 18:07:44 -0700427 } catch (RemoteException | RuntimeException e) {
Eric Jeong09657562020-03-20 16:02:39 -0700428 Log.w(TAG, "Cannot respond to car watchdog daemon (sessionId=" + sessionId + "): " + e);
429 }
430 }
431
Eric Jeong27a178f2020-03-24 16:39:39 -0700432 private void subscribePowerCycleChange() {
433 CarPowerManagementService powerService =
434 CarLocalServices.getService(CarPowerManagementService.class);
435 if (powerService == null) {
436 Log.w(TAG, "Cannot get CarPowerManagementService");
437 return;
438 }
439 powerService.registerListener(new ICarPowerStateListener.Stub() {
440 @Override
441 public void onStateChanged(int state) {
442 int powerCycle;
443 switch (state) {
444 // SHUTDOWN_PREPARE covers suspend and shutdown.
445 case CarPowerStateListener.SHUTDOWN_PREPARE:
446 powerCycle = PowerCycle.POWER_CYCLE_SUSPEND;
447 break;
448 // ON covers resume.
449 case CarPowerStateListener.ON:
450 powerCycle = PowerCycle.POWER_CYCLE_RESUME;
451 // There might be outdated & incorrect info. We should reset them before
452 // starting to do health check.
453 prepareHealthCheck();
454 break;
455 default:
456 return;
457 }
458 try {
459 mCarWatchdogDaemonHelper.notifySystemStateChange(StateType.POWER_CYCLE,
460 powerCycle, /* arg2= */ -1);
Eric Jeong4825ca32020-04-13 18:07:44 -0700461 if (DEBUG) {
462 Log.d(TAG, "Notified car watchdog daemon a power cycle("
463 + powerCycle + ")");
464 }
465 } catch (RemoteException | RuntimeException e) {
Eric Jeong27a178f2020-03-24 16:39:39 -0700466 Log.w(TAG, "Notifying system state change failed: " + e);
467 }
Eric Jeong27a178f2020-03-24 16:39:39 -0700468 }
469 });
470 }
471
Eric Jeong01a28aa2020-03-27 16:43:19 -0700472 private void subscribeUserStateChange() {
473 CarUserService userService = CarLocalServices.getService(CarUserService.class);
474 if (userService == null) {
475 Log.w(TAG, "Cannot get CarUserService");
476 return;
477 }
478 userService.addUserLifecycleListener((event) -> {
479 int userId = event.getUserHandle().getIdentifier();
480 int userState;
481 String userStateDesc;
482 synchronized (mLock) {
483 switch (event.getEventType()) {
484 case USER_LIFECYCLE_EVENT_TYPE_STARTING:
485 mStoppedUser.delete(userId);
486 userState = UserState.USER_STATE_STARTED;
487 userStateDesc = "STARTING";
488 break;
489 case USER_LIFECYCLE_EVENT_TYPE_STOPPED:
490 mStoppedUser.put(userId, true);
491 userState = UserState.USER_STATE_STOPPED;
492 userStateDesc = "STOPPING";
493 break;
494 default:
495 return;
496 }
497 }
498 try {
499 mCarWatchdogDaemonHelper.notifySystemStateChange(StateType.USER_STATE, userId,
500 userState);
Eric Jeong4825ca32020-04-13 18:07:44 -0700501 if (DEBUG) {
502 Log.d(TAG, "Notified car watchdog daemon a user state: userId = " + userId
503 + ", userState = " + userStateDesc);
504 }
505 } catch (RemoteException | RuntimeException e) {
Eric Jeong01a28aa2020-03-27 16:43:19 -0700506 Log.w(TAG, "Notifying system state change failed: " + e);
507 }
Eric Jeong01a28aa2020-03-27 16:43:19 -0700508 });
509 }
510
Eric Jeong27a178f2020-03-24 16:39:39 -0700511 private void prepareHealthCheck() {
512 synchronized (mLock) {
513 for (int timeout : ALL_TIMEOUTS) {
514 SparseArray<ClientInfo> pingedClients = mPingedClientMap.get(timeout);
515 pingedClients.clear();
516 }
517 }
518 }
519
Eric Jeong09657562020-03-20 16:02:39 -0700520 @NonNull
Eric Jeong515009b2020-04-10 17:23:56 -0700521 private int[] toIntArray(@NonNull ArrayList<ClientInfo> list) {
Eric Jeong09657562020-03-20 16:02:39 -0700522 int size = list.size();
523 int[] intArray = new int[size];
524 for (int i = 0; i < size; i++) {
Eric Jeong515009b2020-04-10 17:23:56 -0700525 intArray[i] = list.get(i).pid;
Eric Jeong09657562020-03-20 16:02:39 -0700526 }
527 return intArray;
528 }
529
530 private String timeoutToString(int timeout) {
531 switch (timeout) {
532 case TIMEOUT_CRITICAL:
533 return "critical";
534 case TIMEOUT_MODERATE:
535 return "moderate";
536 case TIMEOUT_NORMAL:
537 return "normal";
538 default:
539 Log.w(TAG, "Unknown timeout value");
540 return "unknown";
541 }
542 }
543
544 private long timeoutToDurationMs(int timeout) {
545 switch (timeout) {
546 case TIMEOUT_CRITICAL:
547 return 3000L;
548 case TIMEOUT_MODERATE:
549 return 5000L;
550 case TIMEOUT_NORMAL:
551 return 10000L;
552 default:
553 Log.w(TAG, "Unknown timeout value");
554 return 10000L;
555 }
556 }
557
Eric Jeong0ee72812020-04-21 15:23:54 -0700558 private static final class ICarWatchdogClientImpl extends ICarWatchdogClient.Stub {
Eric Jeong38ae8212020-01-14 10:25:10 -0800559 private final WeakReference<CarWatchdogService> mService;
560
561 private ICarWatchdogClientImpl(CarWatchdogService service) {
562 mService = new WeakReference<>(service);
563 }
564
565 @Override
566 public void checkIfAlive(int sessionId, int timeout) {
Eric Jeongae2c04c2020-02-21 09:18:31 -0800567 CarWatchdogService service = mService.get();
568 if (service == null) {
Eric Jeong09657562020-03-20 16:02:39 -0700569 Log.w(TAG, "CarWatchdogService is not available");
Eric Jeongae2c04c2020-02-21 09:18:31 -0800570 return;
571 }
Eric Jeong0ee72812020-04-21 15:23:54 -0700572 service.postHealthCheckMessage(sessionId);
Eric Jeong09657562020-03-20 16:02:39 -0700573 }
Jeongik Cha1d04bdd2020-04-08 22:19:52 +0900574
575 @Override
Eric Jeong515009b2020-04-10 17:23:56 -0700576 public void prepareProcessTermination() {
577 Log.w(TAG, "CarWatchdogService is about to be killed by car watchdog daemon");
578 }
579
580 @Override
Jeongik Cha1d04bdd2020-04-08 22:19:52 +0900581 public int getInterfaceVersion() {
582 return this.VERSION;
583 }
584
585 @Override
586 public String getInterfaceHash() {
587 return this.HASH;
588 }
Eric Jeong09657562020-03-20 16:02:39 -0700589 }
590
591 private final class ClientInfo implements IBinder.DeathRecipient {
Eric Jeong01a28aa2020-03-27 16:43:19 -0700592 public final ICarWatchdogClient client;
593 public final int pid;
594 @UserIdInt public final int userId;
595 public final int timeout;
Eric Jeong09657562020-03-20 16:02:39 -0700596 public volatile int sessionId;
597
Eric Jeong01a28aa2020-03-27 16:43:19 -0700598 private ClientInfo(ICarWatchdogClient client, int pid, @UserIdInt int userId, int timeout) {
Eric Jeong09657562020-03-20 16:02:39 -0700599 this.client = client;
600 this.pid = pid;
Eric Jeong01a28aa2020-03-27 16:43:19 -0700601 this.userId = userId;
Eric Jeong09657562020-03-20 16:02:39 -0700602 this.timeout = timeout;
603 }
604
Eric Jeong0ee72812020-04-21 15:23:54 -0700605 @Override
606 public void binderDied() {
607 Log.w(TAG, "Client(pid: " + pid + ") died");
608 onClientDeath(client, timeout);
609 }
610
Eric Jeong09657562020-03-20 16:02:39 -0700611 private void linkToDeath() throws RemoteException {
612 client.asBinder().linkToDeath(this, 0);
613 }
614
615 private void unlinkToDeath() {
616 client.asBinder().unlinkToDeath(this, 0);
617 }
Eric Jeong38ae8212020-01-14 10:25:10 -0800618 }
619}