• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.watchdog;
18 
19 import static android.car.watchdog.CarWatchdogManager.TIMEOUT_CRITICAL;
20 import static android.car.watchdog.CarWatchdogManager.TIMEOUT_MODERATE;
21 import static android.car.watchdog.CarWatchdogManager.TIMEOUT_NORMAL;
22 import static android.car.watchdog.CarWatchdogManager.TimeoutLengthEnum;
23 
24 import static com.android.car.CarServiceUtils.getHandlerThread;
25 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
26 
27 import android.annotation.NonNull;
28 import android.annotation.UserIdInt;
29 import android.automotive.watchdog.internal.ICarWatchdogServiceForSystem;
30 import android.automotive.watchdog.internal.ProcessIdentifier;
31 import android.car.builtin.util.Slogf;
32 import android.car.watchdog.ICarWatchdogServiceCallback;
33 import android.car.watchdoglib.CarWatchdogDaemonHelper;
34 import android.os.Binder;
35 import android.os.Handler;
36 import android.os.IBinder;
37 import android.os.Looper;
38 import android.os.RemoteException;
39 import android.os.SystemClock;
40 import android.os.SystemProperties;
41 import android.os.UserHandle;
42 import android.util.SparseArray;
43 import android.util.SparseBooleanArray;
44 import android.util.proto.ProtoOutputStream;
45 
46 import com.android.car.CarServiceHelperWrapper;
47 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
48 import com.android.car.internal.dep.Trace;
49 import com.android.car.internal.util.IndentingPrintWriter;
50 import com.android.internal.annotations.GuardedBy;
51 
52 import java.util.ArrayList;
53 import java.util.List;
54 import java.util.Optional;
55 
56 /**
57  * Handles clients' health status checking and reporting the statuses to the watchdog daemon.
58  */
59 public final class WatchdogProcessHandler {
60     static final String PROPERTY_RO_CLIENT_HEALTHCHECK_INTERVAL =
61             "ro.carwatchdog.client_healthcheck.interval";
62     static final int MISSING_INT_PROPERTY_VALUE = -1;
63 
64     private static final int[] ALL_TIMEOUTS =
65             { TIMEOUT_CRITICAL, TIMEOUT_MODERATE, TIMEOUT_NORMAL };
66 
67     private final ICarWatchdogServiceForSystem mWatchdogServiceForSystem;
68     private final CarWatchdogDaemonHelper mCarWatchdogDaemonHelper;
69     private final PackageInfoHandler mPackageInfoHandler;
70     private final Handler mMainHandler = new Handler(Looper.getMainLooper());
71     private final Handler mServiceHandler = new Handler(getHandlerThread(
72             CarWatchdogService.class.getSimpleName()).getLooper());
73     private final Object mLock = new Object();
74     /*
75      * Keeps the list of car watchdog client according to timeout:
76      * key => timeout, value => ClientInfo list.
77      * The value of SparseArray is guarded by mLock.
78      */
79     @GuardedBy("mLock")
80     private final SparseArray<ArrayList<ClientInfo>> mClientMap = new SparseArray<>();
81     /*
82      * Keeps the map of car watchdog client being checked by CarWatchdogService according to
83      * timeout: key => timeout, value => ClientInfo map.
84      * The value is also a map: key => session id, value => ClientInfo.
85      */
86     @GuardedBy("mLock")
87     private final SparseArray<SparseArray<ClientInfo>> mPingedClientMap = new SparseArray<>();
88     /*
89      * Keeps whether client health checking is being performed according to timeout:
90      * key => timeout, value => boolean (whether client health checking is being performed).
91      * The value of SparseArray is guarded by mLock.
92      */
93     @GuardedBy("mLock")
94     private final SparseArray<Boolean> mClientCheckInProgress = new SparseArray<>();
95     @GuardedBy("mLock")
96     private final ArrayList<ClientInfo> mClientsNotResponding = new ArrayList<>();
97     // mLastSessionId should only be accessed from the main thread.
98     @GuardedBy("mLock")
99     private int mLastSessionId;
100     @GuardedBy("mLock")
101     private final SparseBooleanArray mStoppedUser = new SparseBooleanArray();
102 
103     private long mOverriddenClientHealthCheckWindowMs = MISSING_INT_PROPERTY_VALUE;
104 
WatchdogProcessHandler(ICarWatchdogServiceForSystem serviceImpl, CarWatchdogDaemonHelper daemonHelper, PackageInfoHandler packageInfoHandler)105     public WatchdogProcessHandler(ICarWatchdogServiceForSystem serviceImpl,
106             CarWatchdogDaemonHelper daemonHelper, PackageInfoHandler packageInfoHandler) {
107         mWatchdogServiceForSystem = serviceImpl;
108         mCarWatchdogDaemonHelper = daemonHelper;
109         mPackageInfoHandler = packageInfoHandler;
110     }
111 
112     /** Initializes the handler. */
init()113     public void init() {
114         synchronized (mLock) {
115             for (int timeout : ALL_TIMEOUTS) {
116                 mClientMap.put(timeout, new ArrayList<ClientInfo>());
117                 mPingedClientMap.put(timeout, new SparseArray<ClientInfo>());
118                 mClientCheckInProgress.put(timeout, false);
119             }
120         }
121         // Overridden timeout value must be greater than  or equal to the maximum possible timeout
122         // value. Otherwise, clients will be pinged more frequently than the guaranteed timeout
123         // duration.
124         int clientHealthCheckWindowSec = SystemProperties.getInt(
125                 PROPERTY_RO_CLIENT_HEALTHCHECK_INTERVAL, MISSING_INT_PROPERTY_VALUE);
126         if (clientHealthCheckWindowSec != MISSING_INT_PROPERTY_VALUE) {
127             mOverriddenClientHealthCheckWindowMs = Math.max(clientHealthCheckWindowSec * 1000L,
128                     getTimeoutDurationMs(TIMEOUT_NORMAL));
129         }
130         if (CarWatchdogService.DEBUG) {
131             Slogf.d(CarWatchdogService.TAG, "WatchdogProcessHandler is initialized");
132         }
133     }
134 
135     /** Dumps its state. */
136     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(IndentingPrintWriter writer)137     public void dump(IndentingPrintWriter writer) {
138         synchronized (mLock) {
139             writer.println("Registered clients");
140             writer.increaseIndent();
141             int count = 1;
142             for (int timeout : ALL_TIMEOUTS) {
143                 ArrayList<ClientInfo> clients = mClientMap.get(timeout);
144                 String timeoutStr = timeoutToString(timeout);
145                 for (ClientInfo clientInfo : clients) {
146                     writer.printf("client #%d: timeout = %s, pid = %d\n", count++, timeoutStr,
147                             clientInfo.pid);
148                 }
149             }
150             writer.printf("Stopped users: ");
151             int size = mStoppedUser.size();
152             if (size > 0) {
153                 writer.printf("%d", mStoppedUser.keyAt(0));
154                 for (int i = 1; i < size; i++) {
155                     writer.printf(", %d", mStoppedUser.keyAt(i));
156                 }
157                 writer.println();
158             } else {
159                 writer.println("none");
160             }
161             writer.decreaseIndent();
162         }
163     }
164 
165     /** Dumps its state in proto format. */
166     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dumpProto(ProtoOutputStream proto)167     public void dumpProto(ProtoOutputStream proto) {
168         synchronized (mLock) {
169             long systemHealthDumpToken = proto.start(
170                     CarWatchdogDumpProto.SYSTEM_HEALTH_DUMP);
171             for (int timeout : ALL_TIMEOUTS) {
172                 ArrayList<ClientInfo> clients = mClientMap.get(timeout);
173                 for (ClientInfo clientInfo : clients) {
174                     long registeredClientsToken = proto.start(
175                             CarWatchdogDumpProto.SystemHealthDump.REGISTERED_CLIENTS);
176                     proto.write(CarWatchdogDumpProto.RegisteredClient.PID, clientInfo.pid);
177                     long userPackageInfoToken = proto.start(
178                             CarWatchdogDumpProto.RegisteredClient.USER_PACKAGE_INFO);
179                     proto.write(UserPackageInfo.USER_ID, clientInfo.getUserId());
180                     proto.write(UserPackageInfo.PACKAGE_NAME, clientInfo.packageName);
181                     proto.end(userPackageInfoToken);
182                     proto.write(CarWatchdogDumpProto.RegisteredClient.HEALTH_CHECK_TIMEOUT,
183                             timeout + 1);
184                     proto.end(registeredClientsToken);
185                 }
186             }
187             for (int i = 0; i < mStoppedUser.size(); i++) {
188                 proto.write(CarWatchdogDumpProto.SystemHealthDump.STOPPED_USERS,
189                         mStoppedUser.keyAt(i));
190             }
191             proto.end(systemHealthDumpToken);
192         }
193     }
194 
195     /** Registers the client callback */
registerClient(ICarWatchdogServiceCallback client, @TimeoutLengthEnum int timeout)196     public void registerClient(ICarWatchdogServiceCallback client, @TimeoutLengthEnum int timeout) {
197         synchronized (mLock) {
198             ArrayList<ClientInfo> clients = mClientMap.get(timeout);
199             if (clients == null) {
200                 Slogf.w(CarWatchdogService.TAG, "Cannot register the client: invalid timeout");
201                 return;
202             }
203             IBinder binder = client.asBinder();
204             for (int i = 0; i < clients.size(); i++) {
205                 ClientInfo clientInfo = clients.get(i);
206                 if (binder == clientInfo.client.asBinder()) {
207                     throw new IllegalStateException(
208                             "Cannot register the client: the client(pid:" + clientInfo.pid
209                                     + ") has been already registered");
210                 }
211             }
212             int pid = Binder.getCallingPid();
213             int callingUid = Binder.getCallingUid();
214             ClientInfo clientInfo = new ClientInfo(client, pid, callingUid, timeout);
215             // PackageInfoHandler may need to retrieve the packageName from system server
216             // using a binder call. Thus, retrieving the packageName from PackageInfoHandler is
217             // posted on the looper and is resolved asynchronously.
218             mServiceHandler.post(() -> {
219                 clientInfo.packageName = mPackageInfoHandler.getNamesForUids(
220                         new int[]{callingUid}).get(callingUid, null);
221             });
222             try {
223                 clientInfo.linkToDeath();
224             } catch (RemoteException e) {
225                 Slogf.w(CarWatchdogService.TAG,
226                         "Cannot register the client: linkToDeath to the client failed");
227                 return;
228             }
229             clients.add(clientInfo);
230             if (CarWatchdogService.DEBUG) {
231                 Slogf.d(CarWatchdogService.TAG, "Registered client: %s", clientInfo);
232             }
233         }
234     }
235 
236     /** Unregisters the previously registered client callback */
unregisterClient(ICarWatchdogServiceCallback client)237     public void unregisterClient(ICarWatchdogServiceCallback client) {
238         ClientInfo clientInfo;
239         synchronized (mLock) {
240             IBinder binder = client.asBinder();
241             // Even if a client did not respond to the latest ping, CarWatchdogService should honor
242             // the unregister request at this point and remove it from all internal caches.
243             // Otherwise, the client might be killed even after unregistering.
244             Optional<ClientInfo> optionalClientInfo = removeFromClientMapsLocked(binder);
245             if (optionalClientInfo.isEmpty()) {
246                 Slogf.w(CarWatchdogService.TAG,
247                         "Cannot unregister the client: the client has not been registered before");
248                 return;
249             }
250             clientInfo = optionalClientInfo.get();
251             for (int i = 0; i < mClientsNotResponding.size(); i++) {
252                 ClientInfo notRespondingClientInfo = mClientsNotResponding.get(i);
253                 if (binder == notRespondingClientInfo.client.asBinder()) {
254                     mClientsNotResponding.remove(i);
255                     break;
256                 }
257             }
258         }
259         if (CarWatchdogService.DEBUG) {
260             Slogf.d(CarWatchdogService.TAG, "Unregistered client: %s", clientInfo);
261         }
262     }
263 
264     @GuardedBy("mLock")
removeFromClientMapsLocked(IBinder binder)265     private Optional<ClientInfo> removeFromClientMapsLocked(IBinder binder) {
266         for (int timeout : ALL_TIMEOUTS) {
267             ArrayList<ClientInfo> clients = mClientMap.get(timeout);
268             for (int i = 0; i < clients.size(); i++) {
269                 ClientInfo clientInfo = clients.get(i);
270                 if (binder != clientInfo.client.asBinder()) {
271                     continue;
272                 }
273                 clientInfo.unlinkToDeath();
274                 clients.remove(i);
275                 SparseArray<ClientInfo> pingedClients = mPingedClientMap.get(timeout);
276                 if (pingedClients != null) {
277                     pingedClients.remove(clientInfo.sessionId);
278                 }
279                 return Optional.of(clientInfo);
280             }
281         }
282         return Optional.empty();
283     }
284 
285     /** Tells the handler that the client is alive. */
tellClientAlive(ICarWatchdogServiceCallback client, int sessionId)286     public void tellClientAlive(ICarWatchdogServiceCallback client, int sessionId) {
287         synchronized (mLock) {
288             for (int timeout : ALL_TIMEOUTS) {
289                 if (!mClientCheckInProgress.get(timeout)) {
290                     continue;
291                 }
292                 SparseArray<ClientInfo> pingedClients = mPingedClientMap.get(timeout);
293                 ClientInfo clientInfo = pingedClients.get(sessionId);
294                 if (clientInfo != null && clientInfo.client.asBinder() == client.asBinder()) {
295                     pingedClients.remove(sessionId);
296                     return;
297                 }
298             }
299         }
300     }
301 
302     /** Updates the user stopped state */
updateUserState(@serIdInt int userId, boolean isStopped)303     public void updateUserState(@UserIdInt int userId, boolean isStopped) {
304         synchronized (mLock) {
305             if (isStopped) {
306                 mStoppedUser.put(userId, true);
307             } else {
308                 mStoppedUser.delete(userId);
309             }
310         }
311     }
312 
313     /** Posts health check message */
postHealthCheckMessage(int sessionId)314     public void postHealthCheckMessage(int sessionId) {
315         mMainHandler.postAtFrontOfQueue(() -> doHealthCheck(sessionId));
316     }
317 
318     /** Returns the registered and alive client count. */
getClientCount(@imeoutLengthEnum int timeout)319     public int getClientCount(@TimeoutLengthEnum int timeout) {
320         synchronized (mLock) {
321             ArrayList<ClientInfo> clients = mClientMap.get(timeout);
322             return clients != null ? clients.size() : 0;
323         }
324     }
325 
326     /** Resets pinged clients before health checking */
prepareHealthCheck()327     public void prepareHealthCheck() {
328         synchronized (mLock) {
329             for (int timeout : ALL_TIMEOUTS) {
330                 SparseArray<ClientInfo> pingedClients = mPingedClientMap.get(timeout);
331                 pingedClients.clear();
332             }
333         }
334     }
335 
336     /**
337      * Asynchronously fetches the AIDL VHAL pid from SystemServer.
338      *
339      * On fetching the AIDL VHAL pid, car watchdog daemon is updated via an async callback.
340      */
asyncFetchAidlVhalPid()341     public void asyncFetchAidlVhalPid() {
342         mServiceHandler.post(() -> {
343             Trace.beginSection("WatchdogProcessHandler.asyncFetchAidlVhalPid");
344             int pid = CarServiceHelperWrapper.getInstance().fetchAidlVhalPid();
345             if (pid < 0) {
346                 Slogf.e(CarWatchdogService.TAG, "Failed to fetch AIDL VHAL pid from"
347                         + " CarServiceHelperService");
348                 Trace.endSection();
349                 return;
350             }
351             try {
352                 mCarWatchdogDaemonHelper.onAidlVhalPidFetched(pid);
353             } catch (RemoteException e) {
354                 Slogf.e(CarWatchdogService.TAG,
355                         "Failed to notify car watchdog daemon of the AIDL VHAL pid");
356             }
357             Trace.endSection();
358         });
359     }
360 
361     /** Enables/disables the watchdog daemon client health check process. */
controlProcessHealthCheck(boolean enable)362     void controlProcessHealthCheck(boolean enable) {
363         Trace.beginSection("WatchdogProcessHandler-healthCheckEnabled-" + enable);
364         try {
365             mCarWatchdogDaemonHelper.controlProcessHealthCheck(enable);
366         } catch (RemoteException e) {
367             Slogf.w(CarWatchdogService.TAG,
368                     "Cannot enable/disable the car watchdog daemon health check process: %s", e);
369         }
370         Trace.endSection();
371     }
372 
onClientDeath(ICarWatchdogServiceCallback client, @TimeoutLengthEnum int timeout)373     private void onClientDeath(ICarWatchdogServiceCallback client, @TimeoutLengthEnum int timeout) {
374         synchronized (mLock) {
375             removeClientLocked(client.asBinder(), timeout);
376         }
377     }
378 
doHealthCheck(int sessionId)379     private void doHealthCheck(int sessionId) {
380         // For critical clients, the response status are checked just before reporting to car
381         // watchdog daemon. For moderate and normal clients, the status are checked after allowed
382         // delay per timeout.
383         Trace.beginSection("WatchdogProcessHandler.doHealthCheck");
384         analyzeClientResponse(TIMEOUT_CRITICAL);
385         reportHealthCheckResult(sessionId);
386         sendPingToClients(TIMEOUT_CRITICAL);
387         sendPingToClientsAndCheck(TIMEOUT_MODERATE);
388         sendPingToClientsAndCheck(TIMEOUT_NORMAL);
389         Trace.endSection();
390     }
391 
analyzeClientResponse(@imeoutLengthEnum int timeout)392     private void analyzeClientResponse(@TimeoutLengthEnum int timeout) {
393         // Clients which are not responding are stored in mClientsNotResponding, and will be dumped
394         // and killed at the next response of CarWatchdogService to car watchdog daemon.
395         Trace.beginSection("WatchdogProcessHandler.analyzeClientResponse");
396         synchronized (mLock) {
397             SparseArray<ClientInfo> pingedClients = mPingedClientMap.get(timeout);
398             for (int i = 0; i < pingedClients.size(); i++) {
399                 ClientInfo clientInfo = pingedClients.valueAt(i);
400                 if (mStoppedUser.get(clientInfo.getUserId())) {
401                     continue;
402                 }
403                 mClientsNotResponding.add(clientInfo);
404                 removeClientLocked(clientInfo.client.asBinder(), timeout);
405             }
406             mClientCheckInProgress.setValueAt(timeout, false);
407         }
408         Trace.endSection();
409     }
410 
sendPingToClients(@imeoutLengthEnum int timeout)411     private void sendPingToClients(@TimeoutLengthEnum int timeout) {
412         Trace.beginSection("WatchdogProcessHandler.sendPingToClients");
413         ArrayList<ClientInfo> clientsToCheck;
414         synchronized (mLock) {
415             SparseArray<ClientInfo> pingedClients = mPingedClientMap.get(timeout);
416             pingedClients.clear();
417             clientsToCheck = new ArrayList<>(mClientMap.get(timeout));
418             for (int i = 0; i < clientsToCheck.size(); i++) {
419                 ClientInfo clientInfo = clientsToCheck.get(i);
420                 if (mStoppedUser.get(clientInfo.getUserId())) {
421                     continue;
422                 }
423                 int sessionId = getNewSessionId();
424                 clientInfo.sessionId = sessionId;
425                 pingedClients.put(sessionId, clientInfo);
426             }
427             mClientCheckInProgress.setValueAt(timeout, true);
428         }
429 
430         for (int i = 0; i < clientsToCheck.size(); i++) {
431             ClientInfo clientInfo = clientsToCheck.get(i);
432             try {
433                 clientInfo.client.onCheckHealthStatus(clientInfo.sessionId, timeout);
434             } catch (RemoteException e) {
435                 Slogf.w(CarWatchdogService.TAG,
436                         "Sending a ping message to client(pid: %d) failed: %s",
437                         clientInfo.pid, e);
438                 synchronized (mLock) {
439                     mPingedClientMap.get(timeout).remove(clientInfo.sessionId);
440                 }
441             }
442         }
443         Trace.endSection();
444     }
445 
sendPingToClientsAndCheck(@imeoutLengthEnum int timeout)446     private void sendPingToClientsAndCheck(@TimeoutLengthEnum int timeout) {
447         synchronized (mLock) {
448             if (mClientCheckInProgress.get(timeout)) {
449                 return;
450             }
451         }
452         sendPingToClients(timeout);
453         mMainHandler.postDelayed(
454                 () -> analyzeClientResponse(timeout), getTimeoutDurationMs(timeout));
455     }
456 
getNewSessionId()457     private int getNewSessionId() {
458         synchronized (mLock) {
459             if (++mLastSessionId <= 0) {
460                 mLastSessionId = 1;
461             }
462             return mLastSessionId;
463         }
464     }
465 
466     @GuardedBy("mLock")
removeClientLocked(IBinder clientBinder, @TimeoutLengthEnum int timeout)467     private void removeClientLocked(IBinder clientBinder, @TimeoutLengthEnum int timeout) {
468         ArrayList<ClientInfo> clients = mClientMap.get(timeout);
469         for (int i = 0; i < clients.size(); i++) {
470             ClientInfo clientInfo = clients.get(i);
471             if (clientBinder == clientInfo.client.asBinder()) {
472                 clients.remove(i);
473                 return;
474             }
475         }
476     }
477 
reportHealthCheckResult(int sessionId)478     private void reportHealthCheckResult(int sessionId) {
479         Trace.beginSection("WatchdogProcessHandler.reportHealthCheckResult");
480         List<ProcessIdentifier> clientsNotResponding;
481         ArrayList<ClientInfo> clientsToNotify;
482         synchronized (mLock) {
483             clientsNotResponding = toProcessIdentifierList(mClientsNotResponding);
484             clientsToNotify = new ArrayList<>(mClientsNotResponding);
485             mClientsNotResponding.clear();
486         }
487         for (int i = 0; i < clientsToNotify.size(); i++) {
488             ClientInfo clientInfo = clientsToNotify.get(i);
489             try {
490                 clientInfo.client.onPrepareProcessTermination();
491             } catch (RemoteException e) {
492                 Slogf.w(CarWatchdogService.TAG,
493                         "Notifying onPrepareProcessTermination to client(pid: %d) failed: %s",
494                         clientInfo.pid, e);
495             }
496         }
497 
498         try {
499             mCarWatchdogDaemonHelper.tellCarWatchdogServiceAlive(
500                     mWatchdogServiceForSystem, clientsNotResponding, sessionId);
501         } catch (RemoteException | RuntimeException e) {
502             Slogf.w(CarWatchdogService.TAG,
503                     "Cannot respond to car watchdog daemon (sessionId=%d): %s", sessionId, e);
504         }
505         Trace.endSection();
506     }
507 
508     @NonNull
toProcessIdentifierList( @onNull ArrayList<ClientInfo> clientInfos)509     private List<ProcessIdentifier> toProcessIdentifierList(
510             @NonNull ArrayList<ClientInfo> clientInfos) {
511         List<ProcessIdentifier> processIdentifiers = new ArrayList<>(clientInfos.size());
512         for (int i = 0; i < clientInfos.size(); i++) {
513             ClientInfo clientInfo = clientInfos.get(i);
514             ProcessIdentifier processIdentifier = new ProcessIdentifier();
515             processIdentifier.pid = clientInfo.pid;
516             processIdentifier.uid = clientInfo.uid;
517             processIdentifier.processName = clientInfo.packageName;
518             processIdentifier.startTimeMillis = clientInfo.startTimeMillis;
519             processIdentifiers.add(processIdentifier);
520         }
521         return processIdentifiers;
522     }
523 
timeoutToString(@imeoutLengthEnum int timeout)524     private String timeoutToString(@TimeoutLengthEnum int timeout) {
525         switch (timeout) {
526             case TIMEOUT_CRITICAL:
527                 return "critical";
528             case TIMEOUT_MODERATE:
529                 return "moderate";
530             case TIMEOUT_NORMAL:
531                 return "normal";
532             default:
533                 Slogf.w(CarWatchdogService.TAG, "Unknown timeout value");
534                 return "unknown";
535         }
536     }
537 
getTimeoutDurationMs(@imeoutLengthEnum int timeout)538     private long getTimeoutDurationMs(@TimeoutLengthEnum int timeout) {
539         if (mOverriddenClientHealthCheckWindowMs != MISSING_INT_PROPERTY_VALUE) {
540             return mOverriddenClientHealthCheckWindowMs;
541         }
542         switch (timeout) {
543             case TIMEOUT_CRITICAL:
544                 return 3000L;
545             case TIMEOUT_MODERATE:
546                 return 5000L;
547             case TIMEOUT_NORMAL:
548                 return 10000L;
549             default:
550                 Slogf.w(CarWatchdogService.TAG, "Unknown timeout value");
551                 return 10000L;
552         }
553     }
554 
555     private final class ClientInfo implements IBinder.DeathRecipient {
556         public final ICarWatchdogServiceCallback client;
557         public final int pid;
558         public final long startTimeMillis;
559         public final int uid;
560         @TimeoutLengthEnum public final int timeout;
561         public volatile int sessionId;
562         public String packageName;
563 
ClientInfo(ICarWatchdogServiceCallback client, int pid, int uid, @TimeoutLengthEnum int timeout)564         ClientInfo(ICarWatchdogServiceCallback client, int pid, int uid,
565                 @TimeoutLengthEnum int timeout) {
566             this.client = client;
567             this.pid = pid;
568             // CarService doesn't have sepolicy access to read per-pid proc files, so it cannot
569             // fetch the pid's actual start time. When a client process registers with
570             // the CarService, it is safe to assume the process is still alive. So, populate
571             // elapsed real time and the consumer (CarServiceHelperService) of this data should
572             // verify that the actual start time is less than the reported start time.
573             this.startTimeMillis = SystemClock.elapsedRealtime();
574             this.timeout = timeout;
575             this.uid = uid;
576         }
577 
578         @Override
binderDied()579         public void binderDied() {
580             Slogf.w(CarWatchdogService.TAG, "Client(pid: %d) died", pid);
581             unlinkToDeath();
582             onClientDeath(client, timeout);
583         }
584 
linkToDeath()585         private void linkToDeath() throws RemoteException {
586             client.asBinder().linkToDeath(this, 0);
587         }
588 
getUserId()589         private int getUserId() {
590             return UserHandle.of(uid).getIdentifier();
591         }
592 
unlinkToDeath()593         private void unlinkToDeath() {
594             client.asBinder().unlinkToDeath(this, 0);
595         }
596 
597         @Override
toString()598         public String toString() {
599             return "ClientInfo{client=" + client + ", pid=" + pid
600                     + ", startTimeMillis=" + startTimeMillis + ", uid=" + uid + ", timeout="
601                     + timeout + ", sessionId=" + sessionId + '}';
602         }
603     }
604 }
605