Merge "Add profile user test to KS" into rvc-dev
diff --git a/car-lib/src/android/car/Car.java b/car-lib/src/android/car/Car.java
index 68f6d8f..4ba587f 100644
--- a/car-lib/src/android/car/Car.java
+++ b/car-lib/src/android/car/Car.java
@@ -1565,7 +1565,8 @@
}
/** @hide */
- Handler getEventHandler() {
+ @VisibleForTesting
+ public Handler getEventHandler() {
return mEventHandler;
}
diff --git a/car-lib/src/android/car/CarAppFocusManager.java b/car-lib/src/android/car/CarAppFocusManager.java
index ef9ddf4..3d4d90d 100644
--- a/car-lib/src/android/car/CarAppFocusManager.java
+++ b/car-lib/src/android/car/CarAppFocusManager.java
@@ -21,6 +21,8 @@
import android.os.IBinder;
import android.os.RemoteException;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
@@ -122,7 +124,8 @@
/**
* @hide
*/
- CarAppFocusManager(Car car, IBinder service) {
+ @VisibleForTesting
+ public CarAppFocusManager(Car car, IBinder service) {
super(car);
mService = IAppFocus.Stub.asInterface(service);
}
diff --git a/car-lib/src/android/car/app/CarActivityView.java b/car-lib/src/android/car/app/CarActivityView.java
index b63d08b..6a538d6 100644
--- a/car-lib/src/android/car/app/CarActivityView.java
+++ b/car-lib/src/android/car/app/CarActivityView.java
@@ -39,6 +39,7 @@
// volatile, since mUserActivityViewCallback can be accessed from Main and Binder thread.
@Nullable private volatile StateCallback mUserActivityViewCallback;
+ @Nullable private Car mCar;
@Nullable private CarUxRestrictionsManager mUxRestrictionsManager;
private int mVirtualDisplayId = Display.INVALID_DISPLAY;
@@ -59,21 +60,6 @@
Context context, AttributeSet attrs, int defStyle, boolean singleTaskInstance) {
super(context, attrs, defStyle, singleTaskInstance);
super.setCallback(new CarActivityViewCallback());
- Car.createCar(mContext, /*handler=*/ null,
- Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT,
- (car, ready) -> {
- // Expect to be called in the main thread, since passed a 'null' handler
- // in Car.createCar().
- if (!ready) return;
- mUxRestrictionsManager = (CarUxRestrictionsManager) car.getCarManager(
- Car.CAR_UX_RESTRICTION_SERVICE);
- if (mVirtualDisplayId != Display.INVALID_DISPLAY) {
- // When the CarService is reconnected, we'd like to report the physical
- // display id again, since the previously reported mapping could be gone.
- reportPhysicalDisplayId(
- mUxRestrictionsManager, mVirtualDisplayId, mContext.getDisplayId());
- }
- });
}
@Override
@@ -154,4 +140,30 @@
}
}
}
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mCar = Car.createCar(mContext, /*handler=*/ null,
+ Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT,
+ (car, ready) -> {
+ // Expect to be called in the main thread, since passed a 'null' handler
+ // in Car.createCar().
+ if (!ready) return;
+ mUxRestrictionsManager = (CarUxRestrictionsManager) car.getCarManager(
+ Car.CAR_UX_RESTRICTION_SERVICE);
+ if (mVirtualDisplayId != Display.INVALID_DISPLAY) {
+ // When the CarService is reconnected, we'd like to report the physical
+ // display id again, since the previously reported mapping could be gone.
+ reportPhysicalDisplayId(
+ mUxRestrictionsManager, mVirtualDisplayId, mContext.getDisplayId());
+ }
+ });
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (mCar != null) mCar.disconnect();
+ }
}
diff --git a/car-lib/src/android/car/watchdog/CarWatchdogManager.java b/car-lib/src/android/car/watchdog/CarWatchdogManager.java
index c49a81a..fff1283 100644
--- a/car-lib/src/android/car/watchdog/CarWatchdogManager.java
+++ b/car-lib/src/android/car/watchdog/CarWatchdogManager.java
@@ -96,8 +96,8 @@
* respond by calling {@link CarWatchdogManager.tellClientAlive} within timeout. If they don't
* respond, car watchdog server reports the current state and kills them.
*
- * <p>Before car watchdog server kills the client, it calls onPrepareProcessKill to allow them
- * to prepare the termination. They will be killed in 1 second.
+ * <p>Before car watchdog server kills the client, it calls onPrepareProcessTermination to allow
+ * them to prepare the termination. They will be killed in 1 second.
*/
public abstract static class CarWatchdogClientCallback {
/**
@@ -124,8 +124,6 @@
* <p>The callback method is called at the Executor which is specifed in {@link
* #registerClient}.
*/
- // TODO(b/150006093): After adding a callback to ICarWatchdogClient, subsequent
- // implementation should be done in CarWatchdogService and CarWatchdogManager.
public void onPrepareProcessTermination() {}
}
@@ -305,6 +303,20 @@
}
}
+ private void notifyProcessTermination() {
+ CarWatchdogClientCallback client;
+ Executor executor;
+ synchronized (mLock) {
+ if (mRegisteredClient == null) {
+ Log.w(TAG, "Cannot notify the client. The client has not been registered.");
+ return;
+ }
+ client = mRegisteredClient;
+ executor = mCallbackExecutor;
+ }
+ executor.execute(() -> client.onPrepareProcessTermination());
+ }
+
/** @hide */
private static final class ICarWatchdogClientImpl extends ICarWatchdogClient.Stub {
private final WeakReference<CarWatchdogManager> mManager;
@@ -322,6 +334,14 @@
}
@Override
+ public void prepareProcessTermination() {
+ CarWatchdogManager manager = mManager.get();
+ if (manager != null) {
+ manager.notifyProcessTermination();
+ }
+ }
+
+ @Override
public int getInterfaceVersion() {
return this.VERSION;
}
diff --git a/computepipe/runner/RunnerComponent.cpp b/computepipe/runner/RunnerComponent.cpp
index b8375a5..d3d1482 100644
--- a/computepipe/runner/RunnerComponent.cpp
+++ b/computepipe/runner/RunnerComponent.cpp
@@ -49,6 +49,7 @@
config.set_input_config_id(mInputConfigId);
config.set_termination_id(mTerminationId);
config.set_offload_id(mOffloadId);
+ config.set_profiling_type(mProfilingType);
for (auto it : mOutputConfigs) {
(*config.mutable_output_options())[it.first] = it.second;
}
diff --git a/computepipe/runner/client_interface/DebuggerImpl.cpp b/computepipe/runner/client_interface/DebuggerImpl.cpp
index ffd4820..67d516a 100644
--- a/computepipe/runner/client_interface/DebuggerImpl.cpp
+++ b/computepipe/runner/client_interface/DebuggerImpl.cpp
@@ -37,7 +37,7 @@
using ::ndk::ScopedAStatus;
-constexpr std::chrono::milliseconds kProfilingDataReadTimeout = 10ms;
+constexpr std::chrono::milliseconds kProfilingDataReadTimeout = 50ms;
proto::ProfilingType ToProtoProfilingType(PipeProfilingType type) {
switch (type) {
diff --git a/computepipe/runner/engine/DefaultEngine.cpp b/computepipe/runner/engine/DefaultEngine.cpp
index 9f1531b..f10f74a 100644
--- a/computepipe/runner/engine/DefaultEngine.cpp
+++ b/computepipe/runner/engine/DefaultEngine.cpp
@@ -636,18 +636,18 @@
mCommandQueue.pop();
switch (ec.cmdType) {
case EngineCommand::Type::BROADCAST_CONFIG:
- LOG(INFO) << "Engine::Received broacast config request";
+ LOG(INFO) << "Engine::Received broadcast config request";
(void)broadcastClientConfig();
break;
case EngineCommand::Type::BROADCAST_START_RUN:
- LOG(INFO) << "Engine::Received broacast run request";
+ LOG(INFO) << "Engine::Received broadcast run request";
(void)broadcastStartRun();
break;
case EngineCommand::Type::BROADCAST_INITIATE_STOP:
if (ec.source.find("ClientInterface") != std::string::npos) {
mStopFromClient = true;
}
- LOG(INFO) << "Engine::Received broacast stop with flush request";
+ LOG(INFO) << "Engine::Received broadcast stop with flush request";
broadcastStopWithFlush();
break;
case EngineCommand::Type::POLL_COMPLETE:
@@ -683,7 +683,8 @@
break;
case EngineCommand::Type::READ_PROFILING:
std::string debugData;
- if (mGraph && (mCurrentPhase == kConfigPhase || mCurrentPhase == kRunPhase)) {
+ if (mGraph && (mCurrentPhase == kConfigPhase || mCurrentPhase == kRunPhase
+ || mCurrentPhase == kStopPhase)) {
debugData = mGraph->GetDebugInfo();
}
if (mClient) {
diff --git a/service/src/com/android/car/AppFocusService.java b/service/src/com/android/car/AppFocusService.java
index 27161a5..42f7ac9 100644
--- a/service/src/com/android/car/AppFocusService.java
+++ b/service/src/com/android/car/AppFocusService.java
@@ -51,11 +51,13 @@
private final Object mLock = new Object();
+ @VisibleForTesting
@GuardedBy("mLock")
- private final ClientHolder mAllChangeClients;
+ final ClientHolder mAllChangeClients;
+ @VisibleForTesting
@GuardedBy("mLock")
- private final OwnershipClientHolder mAllOwnershipClients;
+ final OwnershipClientHolder mAllOwnershipClients;
/** K: appType, V: client owning it */
@GuardedBy("mLock")
@@ -71,16 +73,19 @@
mAllBinderEventHandler = bInterface -> { /* nothing to do.*/ };
@GuardedBy("mLock")
- private DispatchHandler mDispatchHandler;
+ private final DispatchHandler mDispatchHandler;
@GuardedBy("mLock")
- private HandlerThread mHandlerThread;
+ private final HandlerThread mHandlerThread;
public AppFocusService(Context context,
SystemActivityMonitoringService systemActivityMonitoringService) {
mSystemActivityMonitoringService = systemActivityMonitoringService;
mAllChangeClients = new ClientHolder(mAllBinderEventHandler);
mAllOwnershipClients = new OwnershipClientHolder(this);
+ mHandlerThread = new HandlerThread(AppFocusService.class.getSimpleName());
+ mHandlerThread.start();
+ mDispatchHandler = new DispatchHandler(mHandlerThread.getLooper());
}
@Override
@@ -230,11 +235,7 @@
@Override
public void init() {
- synchronized (mLock) {
- mHandlerThread = new HandlerThread(AppFocusService.class.getSimpleName());
- mHandlerThread.start();
- mDispatchHandler = new DispatchHandler(mHandlerThread.getLooper());
- }
+ // nothing to do
}
@VisibleForTesting
@@ -247,17 +248,6 @@
@Override
public void release() {
synchronized (mLock) {
- if (mDispatchHandler == null) {
- return;
- }
- mHandlerThread.quitSafely();
- try {
- mHandlerThread.join(1000);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- Log.e(CarLog.TAG_APP_FOCUS, "Timeout while waiting for handler thread to join.");
- }
- mDispatchHandler = null;
mAllChangeClients.clear();
mAllOwnershipClients.clear();
mFocusOwners.clear();
@@ -372,13 +362,15 @@
}
}
- private static class ClientHolder extends BinderInterfaceContainer<IAppFocusListener> {
+ @VisibleForTesting
+ static class ClientHolder extends BinderInterfaceContainer<IAppFocusListener> {
private ClientHolder(BinderEventHandler<IAppFocusListener> holder) {
super(holder);
}
}
- private static class OwnershipClientHolder extends
+ @VisibleForTesting
+ static class OwnershipClientHolder extends
BinderInterfaceContainer<IAppFocusOwnershipCallback> {
private OwnershipClientHolder(AppFocusService service) {
super(service);
diff --git a/service/src/com/android/car/BinderInterfaceContainer.java b/service/src/com/android/car/BinderInterfaceContainer.java
index b97db35..32f7eb9 100644
--- a/service/src/com/android/car/BinderInterfaceContainer.java
+++ b/service/src/com/android/car/BinderInterfaceContainer.java
@@ -181,9 +181,9 @@
}
private void handleBinderDeath(BinderInterface<T> bInterface) {
- removeBinder(bInterface.binderInterface);
if (mEventHandler != null) {
mEventHandler.onBinderDeath(bInterface);
}
+ removeBinder(bInterface.binderInterface);
}
}
diff --git a/service/src/com/android/car/watchdog/CarWatchdogService.java b/service/src/com/android/car/watchdog/CarWatchdogService.java
index d24e569..21ac3e6 100644
--- a/service/src/com/android/car/watchdog/CarWatchdogService.java
+++ b/service/src/com/android/car/watchdog/CarWatchdogService.java
@@ -107,7 +107,7 @@
@GuardedBy("mLock")
private final SparseArray<Boolean> mClientCheckInProgress = new SparseArray<>();
@GuardedBy("mLock")
- private final ArrayList<Integer> mClientsNotResponding = new ArrayList<>();
+ private final ArrayList<ClientInfo> mClientsNotResponding = new ArrayList<>();
@GuardedBy("mMainHandler")
private int mLastSessionId;
@GuardedBy("mMainHandler")
@@ -324,14 +324,13 @@
// and killed at the next response of CarWatchdogService to car watchdog daemon.
SparseArray<ClientInfo> pingedClients = mPingedClientMap.get(timeout);
synchronized (mLock) {
- // Unhealthy clients are eventually removed from the list through binderDied when they
- // are killed.
for (int i = 0; i < pingedClients.size(); i++) {
ClientInfo clientInfo = pingedClients.valueAt(i);
if (mStoppedUser.get(clientInfo.userId)) {
continue;
}
- mClientsNotResponding.add(clientInfo.pid);
+ mClientsNotResponding.add(clientInfo);
+ removeClientLocked(clientInfo.client.asBinder(), timeout);
}
mClientCheckInProgress.setValueAt(timeout, false);
}
@@ -399,10 +398,22 @@
private void reportHealthCheckResult(int sessionId) {
int[] clientsNotResponding;
+ ArrayList<ClientInfo> clientsToNotify;
synchronized (mLock) {
clientsNotResponding = toIntArray(mClientsNotResponding);
+ clientsToNotify = new ArrayList<>(mClientsNotResponding);
mClientsNotResponding.clear();
}
+ for (int i = 0; i < clientsToNotify.size(); i++) {
+ ClientInfo clientInfo = clientsToNotify.get(i);
+ try {
+ clientInfo.client.prepareProcessTermination();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Notifying prepareProcessTermination to client(pid: " + clientInfo.pid
+ + ") failed: " + e);
+ }
+ }
+
try {
mCarWatchdogDaemonHelper.tellMediatorAlive(mWatchdogClient, clientsNotResponding,
sessionId);
@@ -500,11 +511,11 @@
}
@NonNull
- private int[] toIntArray(@NonNull ArrayList<Integer> list) {
+ private int[] toIntArray(@NonNull ArrayList<ClientInfo> list) {
int size = list.size();
int[] intArray = new int[size];
for (int i = 0; i < size; i++) {
- intArray[i] = list.get(i);
+ intArray[i] = list.get(i).pid;
}
return intArray;
}
@@ -556,6 +567,11 @@
}
@Override
+ public void prepareProcessTermination() {
+ Log.w(TAG, "CarWatchdogService is about to be killed by car watchdog daemon");
+ }
+
+ @Override
public int getInterfaceVersion() {
return this.VERSION;
}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/CarWatchdogClient.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/CarWatchdogClient.java
index 3235a60..3f696f7 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/CarWatchdogClient.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/CarWatchdogClient.java
@@ -49,7 +49,7 @@
@Override
public void onPrepareProcessTermination() {
- Log.i(TAG, "This process is being terminated by Car watchdog");
+ Log.w(TAG, "This process is being terminated by car watchdog");
}
};
private final ExecutorService mCallbackExecutor = Executors.newFixedThreadPool(1);
diff --git a/tests/carservice_unit_test/src/android/car/watchdoglib/CarWatchdogDaemonHelperTest.java b/tests/carservice_unit_test/src/android/car/watchdoglib/CarWatchdogDaemonHelperTest.java
index acd149a..6cf82bd 100644
--- a/tests/carservice_unit_test/src/android/car/watchdoglib/CarWatchdogDaemonHelperTest.java
+++ b/tests/carservice_unit_test/src/android/car/watchdoglib/CarWatchdogDaemonHelperTest.java
@@ -211,6 +211,9 @@
public void checkIfAlive(int sessionId, int timeout) {}
@Override
+ public void prepareProcessTermination() {}
+
+ @Override
public int getInterfaceVersion() {
return this.VERSION;
}
diff --git a/tests/carservice_unit_test/src/com/android/car/AppFocusServiceTest.java b/tests/carservice_unit_test/src/com/android/car/AppFocusServiceTest.java
new file mode 100644
index 0000000..0e7731b
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/AppFocusServiceTest.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+
+import android.car.Car;
+import android.car.CarAppFocusManager;
+import android.car.IAppFocusListener;
+import android.car.IAppFocusOwnershipCallback;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(MockitoJUnitRunner.class)
+public class AppFocusServiceTest {
+
+ private static final long WAIT_TIMEOUT_MS = 500;
+
+ private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private SystemActivityMonitoringService mSystemActivityMonitoringService;
+ @Mock
+ private Car mCar;
+
+ private AppFocusService mService;
+ private CarAppFocusManager mCarAppFocusManager1;
+ private CarAppFocusManager mCarAppFocusManager2;
+
+ private AppFocusChangedListener mAppFocusChangedListener1 = new AppFocusChangedListener();
+
+ private AppFocusOwnershipCallback mAppFocusOwnershipCallback1 = new AppFocusOwnershipCallback();
+
+ @Before
+ public void setUp() {
+ mService = new AppFocusService(mContext, mSystemActivityMonitoringService);
+ mService.init();
+ doReturn(mMainHandler).when(mCar).getEventHandler();
+ mCarAppFocusManager1 = new CarAppFocusManager(mCar, mService.asBinder());
+ mCarAppFocusManager2 = new CarAppFocusManager(mCar, mService.asBinder());
+ }
+
+ @Test
+ public void testSingleOwner() throws Exception {
+ mCarAppFocusManager2.addFocusListener(mAppFocusChangedListener1,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+
+ int r = mCarAppFocusManager1.requestAppFocus(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION,
+ mAppFocusOwnershipCallback1);
+ assertThat(r).isEqualTo(CarAppFocusManager.APP_FOCUS_REQUEST_SUCCEEDED);
+ assertThat(mCarAppFocusManager1.isOwningFocus(mAppFocusOwnershipCallback1,
+ CarAppFocusManager.APP_FOCUS_REQUEST_SUCCEEDED)).isTrue();
+ waitForNavFocusChangeAndAssert(mAppFocusChangedListener1, true);
+
+ mAppFocusChangedListener1.resetWait();
+ mCarAppFocusManager1.abandonAppFocus(mAppFocusOwnershipCallback1,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ assertThat(mCarAppFocusManager1.isOwningFocus(mAppFocusOwnershipCallback1,
+ CarAppFocusManager.APP_FOCUS_REQUEST_SUCCEEDED)).isFalse();
+ waitForNavFocusChangeAndAssert(mAppFocusChangedListener1, false);
+ }
+
+ private void waitForNavFocusChangeAndAssert(AppFocusChangedListener listener, boolean isActive)
+ throws Exception {
+ listener.waitForEvent();
+ if (isActive) {
+ assertThat(listener.mLastActive).isTrue();
+ } else {
+ assertThat(listener.mLastActive).isFalse();
+ }
+ assertThat(listener.mLastAppType).isEqualTo(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ }
+
+ @Test
+ public void testOwnerBinderDeath() throws Exception {
+ mCarAppFocusManager2.addFocusListener(mAppFocusChangedListener1,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+
+ int r = mCarAppFocusManager1.requestAppFocus(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION,
+ mAppFocusOwnershipCallback1);
+ assertThat(r).isEqualTo(CarAppFocusManager.APP_FOCUS_REQUEST_SUCCEEDED);
+ assertThat(mCarAppFocusManager1.isOwningFocus(mAppFocusOwnershipCallback1,
+ CarAppFocusManager.APP_FOCUS_REQUEST_SUCCEEDED)).isTrue();
+ waitForNavFocusChangeAndAssert(mAppFocusChangedListener1, true);
+
+ assertThat(mService.mAllOwnershipClients.getInterfaces()).hasSize(1);
+ BinderInterfaceContainer.BinderInterface<IAppFocusOwnershipCallback> binder =
+ mService.mAllOwnershipClients.getInterfaces().iterator().next();
+ // Now fake binder death
+ mAppFocusChangedListener1.resetWait();
+ binder.binderDied();
+ assertThat(mService.mAllOwnershipClients.getInterfaces()).isEmpty();
+ waitForNavFocusChangeAndAssert(mAppFocusChangedListener1, false);
+ }
+
+ @Test
+ public void testListenerBinderDeath() throws Exception {
+
+ mCarAppFocusManager1.addFocusListener(mAppFocusChangedListener1,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ assertThat(mService.mAllChangeClients.getInterfaces()).hasSize(1);
+ BinderInterfaceContainer.BinderInterface<IAppFocusListener> binder =
+ mService.mAllChangeClients.getInterfaces().iterator().next();
+ binder.binderDied();
+ assertThat(mService.mAllChangeClients.getInterfaces()).isEmpty();
+ }
+
+ private class AppFocusChangedListener implements CarAppFocusManager.OnAppFocusChangedListener {
+
+ private final Semaphore mSemaphore = new Semaphore(0);
+ private int mLastAppType;
+ private boolean mLastActive;
+
+ @Override
+ public void onAppFocusChanged(int appType, boolean active) {
+ mLastAppType = appType;
+ mLastActive = active;
+ mSemaphore.release();
+ }
+
+ public void waitForEvent() throws Exception {
+ assertThat(mSemaphore.tryAcquire(WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
+ }
+
+ public void resetWait() {
+ mSemaphore.drainPermits();
+ }
+ }
+
+ private class AppFocusOwnershipCallback implements
+ CarAppFocusManager.OnAppFocusOwnershipCallback {
+
+ private final Semaphore mSemaphore = new Semaphore(0);
+ private int mGrantedAppTypes;
+
+ @Override
+ public void onAppFocusOwnershipLost(int appType) {
+ mGrantedAppTypes = mGrantedAppTypes & ~appType;
+ mSemaphore.release();
+ }
+
+ @Override
+ public void onAppFocusOwnershipGranted(int appType) {
+ mGrantedAppTypes = mGrantedAppTypes | appType;
+ mSemaphore.release();
+ }
+
+ public void waitForEvent() throws Exception {
+ mSemaphore.tryAcquire(WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ }
+
+ public void resetWait() {
+ mSemaphore.drainPermits();
+ }
+ }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/watchdog/CarWatchdogServiceTest.java b/tests/carservice_unit_test/src/com/android/car/watchdog/CarWatchdogServiceTest.java
index cfc9802..4467d61 100644
--- a/tests/carservice_unit_test/src/com/android/car/watchdog/CarWatchdogServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/watchdog/CarWatchdogServiceTest.java
@@ -226,6 +226,9 @@
}
@Override
+ public void prepareProcessTermination() {}
+
+ @Override
public int getInterfaceVersion() {
return this.VERSION;
}
diff --git a/watchdog/aidl/Android.bp b/watchdog/aidl/Android.bp
index 25564c1..e17c87b 100644
--- a/watchdog/aidl/Android.bp
+++ b/watchdog/aidl/Android.bp
@@ -25,5 +25,7 @@
enabled: true,
},
},
- versions: ["1"],
+ versions: [
+ "2",
+ ],
}
diff --git a/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/2/.hash b/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/2/.hash
new file mode 100644
index 0000000..f024209
--- /dev/null
+++ b/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/2/.hash
@@ -0,0 +1 @@
+f7adf2ef96b380c7fde3919f565eb764986bdcdd
diff --git a/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/2/android/automotive/watchdog/BootPhase.aidl b/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/2/android/automotive/watchdog/BootPhase.aidl
new file mode 100644
index 0000000..f8570de
--- /dev/null
+++ b/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/2/android/automotive/watchdog/BootPhase.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.automotive.watchdog;
+@Backing(type="int") @VintfStability
+enum BootPhase {
+ BOOT_COMPLETED = 1000,
+}
diff --git a/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/2/android/automotive/watchdog/ICarWatchdog.aidl b/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/2/android/automotive/watchdog/ICarWatchdog.aidl
new file mode 100644
index 0000000..61d4f32
--- /dev/null
+++ b/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/2/android/automotive/watchdog/ICarWatchdog.aidl
@@ -0,0 +1,31 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.automotive.watchdog;
+@VintfStability
+interface ICarWatchdog {
+ void registerClient(in android.automotive.watchdog.ICarWatchdogClient client, in android.automotive.watchdog.TimeoutLength timeout);
+ void unregisterClient(in android.automotive.watchdog.ICarWatchdogClient client);
+ void registerMediator(in android.automotive.watchdog.ICarWatchdogClient mediator);
+ void unregisterMediator(in android.automotive.watchdog.ICarWatchdogClient mediator);
+ void registerMonitor(in android.automotive.watchdog.ICarWatchdogMonitor monitor);
+ void unregisterMonitor(in android.automotive.watchdog.ICarWatchdogMonitor monitor);
+ void tellClientAlive(in android.automotive.watchdog.ICarWatchdogClient client, in int sessionId);
+ void tellMediatorAlive(in android.automotive.watchdog.ICarWatchdogClient mediator, in int[] clientsNotResponding, in int sessionId);
+ void tellDumpFinished(in android.automotive.watchdog.ICarWatchdogMonitor monitor, in int pid);
+ void notifySystemStateChange(in android.automotive.watchdog.StateType type, in int arg1, in int arg2);
+}
diff --git a/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/2/android/automotive/watchdog/ICarWatchdogClient.aidl b/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/2/android/automotive/watchdog/ICarWatchdogClient.aidl
new file mode 100644
index 0000000..9768565
--- /dev/null
+++ b/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/2/android/automotive/watchdog/ICarWatchdogClient.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.automotive.watchdog;
+@VintfStability
+interface ICarWatchdogClient {
+ oneway void checkIfAlive(in int sessionId, in android.automotive.watchdog.TimeoutLength timeout);
+ oneway void prepareProcessTermination();
+}
diff --git a/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/2/android/automotive/watchdog/ICarWatchdogMonitor.aidl b/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/2/android/automotive/watchdog/ICarWatchdogMonitor.aidl
new file mode 100644
index 0000000..f13af14
--- /dev/null
+++ b/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/2/android/automotive/watchdog/ICarWatchdogMonitor.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.automotive.watchdog;
+@VintfStability
+interface ICarWatchdogMonitor {
+ oneway void onClientsNotResponding(in int[] pids);
+}
diff --git a/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/2/android/automotive/watchdog/PowerCycle.aidl b/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/2/android/automotive/watchdog/PowerCycle.aidl
new file mode 100644
index 0000000..2cfd03e
--- /dev/null
+++ b/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/2/android/automotive/watchdog/PowerCycle.aidl
@@ -0,0 +1,25 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.automotive.watchdog;
+@Backing(type="int") @VintfStability
+enum PowerCycle {
+ POWER_CYCLE_SHUTDOWN = 0,
+ POWER_CYCLE_SUSPEND = 1,
+ POWER_CYCLE_RESUME = 2,
+ NUM_POWER_CYLES = 3,
+}
diff --git a/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/2/android/automotive/watchdog/StateType.aidl b/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/2/android/automotive/watchdog/StateType.aidl
new file mode 100644
index 0000000..52cd08e
--- /dev/null
+++ b/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/2/android/automotive/watchdog/StateType.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.automotive.watchdog;
+@Backing(type="int") @VintfStability
+enum StateType {
+ POWER_CYCLE = 0,
+ USER_STATE = 1,
+ BOOT_PHASE = 2,
+}
diff --git a/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/2/android/automotive/watchdog/TimeoutLength.aidl b/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/2/android/automotive/watchdog/TimeoutLength.aidl
new file mode 100644
index 0000000..e4be851
--- /dev/null
+++ b/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/2/android/automotive/watchdog/TimeoutLength.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.automotive.watchdog;
+@Backing(type="int") @VintfStability
+enum TimeoutLength {
+ TIMEOUT_CRITICAL = 0,
+ TIMEOUT_MODERATE = 1,
+ TIMEOUT_NORMAL = 2,
+}
diff --git a/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/2/android/automotive/watchdog/UserState.aidl b/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/2/android/automotive/watchdog/UserState.aidl
new file mode 100644
index 0000000..39dcc84
--- /dev/null
+++ b/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/2/android/automotive/watchdog/UserState.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
+// edit this file. It looks like you are doing that because you have modified
+// an AIDL interface in a backward-incompatible way, e.g., deleting a function
+// from an interface or a field from a parcelable and it broke the build. That
+// breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.automotive.watchdog;
+@Backing(type="int") @VintfStability
+enum UserState {
+ USER_STATE_STARTED = 0,
+ USER_STATE_STOPPED = 1,
+ NUM_USER_STATES = 2,
+}
diff --git a/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/current/android/automotive/watchdog/ICarWatchdogClient.aidl b/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/current/android/automotive/watchdog/ICarWatchdogClient.aidl
index d934ad0..9768565 100644
--- a/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/current/android/automotive/watchdog/ICarWatchdogClient.aidl
+++ b/watchdog/aidl/aidl_api/carwatchdog_aidl_interface/current/android/automotive/watchdog/ICarWatchdogClient.aidl
@@ -19,4 +19,5 @@
@VintfStability
interface ICarWatchdogClient {
oneway void checkIfAlive(in int sessionId, in android.automotive.watchdog.TimeoutLength timeout);
+ oneway void prepareProcessTermination();
}
diff --git a/watchdog/aidl/android/automotive/watchdog/ICarWatchdogClient.aidl b/watchdog/aidl/android/automotive/watchdog/ICarWatchdogClient.aidl
index 1d33957..958b620 100644
--- a/watchdog/aidl/android/automotive/watchdog/ICarWatchdogClient.aidl
+++ b/watchdog/aidl/android/automotive/watchdog/ICarWatchdogClient.aidl
@@ -31,4 +31,9 @@
* @param timeout Final timeout given by the server based on client request.
*/
void checkIfAlive(in int sessionId, in TimeoutLength timeout);
+
+ /**
+ * Notify the client that it will be forcedly terminated in 1 second.
+ */
+ void prepareProcessTermination();
}
diff --git a/watchdog/server/src/IoPerfCollection.cpp b/watchdog/server/src/IoPerfCollection.cpp
index 0e45996..63d47ef 100644
--- a/watchdog/server/src/IoPerfCollection.cpp
+++ b/watchdog/server/src/IoPerfCollection.cpp
@@ -77,32 +77,75 @@
}
struct UidProcessStats {
+ struct ProcessInfo {
+ std::string comm = "";
+ uint64_t count = 0;
+ };
uint64_t uid = 0;
uint32_t ioBlockedTasksCnt = 0;
uint32_t totalTasksCnt = 0;
uint64_t majorFaults = 0;
+ std::vector<ProcessInfo> topNIoBlockedProcesses = {};
+ std::vector<ProcessInfo> topNMajorFaultProcesses = {};
};
-std::unordered_map<uint32_t, UidProcessStats> getUidProcessStats(
- const std::vector<ProcessStats>& processStats) {
- std::unordered_map<uint32_t, UidProcessStats> uidProcessStats;
+std::unique_ptr<std::unordered_map<uint32_t, UidProcessStats>> getUidProcessStats(
+ const std::vector<ProcessStats>& processStats, int topNStatsPerSubCategory) {
+ std::unique_ptr<std::unordered_map<uint32_t, UidProcessStats>> uidProcessStats(
+ new std::unordered_map<uint32_t, UidProcessStats>());
for (const auto& stats : processStats) {
if (stats.uid < 0) {
continue;
}
uint32_t uid = static_cast<uint32_t>(stats.uid);
- if (uidProcessStats.find(uid) == uidProcessStats.end()) {
- uidProcessStats[uid] = UidProcessStats{.uid = uid};
+ if (uidProcessStats->find(uid) == uidProcessStats->end()) {
+ (*uidProcessStats)[uid] = UidProcessStats{
+ .uid = uid,
+ .topNIoBlockedProcesses = std::vector<
+ UidProcessStats::ProcessInfo>(topNStatsPerSubCategory,
+ UidProcessStats::ProcessInfo{}),
+ .topNMajorFaultProcesses = std::vector<
+ UidProcessStats::ProcessInfo>(topNStatsPerSubCategory,
+ UidProcessStats::ProcessInfo{}),
+ };
}
- auto& curUidProcessStats = uidProcessStats[uid];
+ auto& curUidProcessStats = (*uidProcessStats)[uid];
// Top-level process stats has the aggregated major page faults count and this should be
// persistent across thread creation/termination. Thus use the value from this field.
curUidProcessStats.majorFaults += stats.process.majorFaults;
curUidProcessStats.totalTasksCnt += stats.threads.size();
// The process state is the same as the main thread state. Thus to avoid double counting
// ignore the process state.
+ uint32_t ioBlockedTasksCnt = 0;
for (const auto& threadStat : stats.threads) {
- curUidProcessStats.ioBlockedTasksCnt += threadStat.second.state == "D" ? 1 : 0;
+ ioBlockedTasksCnt += threadStat.second.state == "D" ? 1 : 0;
+ }
+ curUidProcessStats.ioBlockedTasksCnt += ioBlockedTasksCnt;
+ for (auto it = curUidProcessStats.topNIoBlockedProcesses.begin();
+ it != curUidProcessStats.topNIoBlockedProcesses.end(); ++it) {
+ if (it->count < ioBlockedTasksCnt) {
+ curUidProcessStats.topNIoBlockedProcesses
+ .emplace(it,
+ UidProcessStats::ProcessInfo{
+ .comm = stats.process.comm,
+ .count = ioBlockedTasksCnt,
+ });
+ curUidProcessStats.topNIoBlockedProcesses.pop_back();
+ break;
+ }
+ }
+ for (auto it = curUidProcessStats.topNMajorFaultProcesses.begin();
+ it != curUidProcessStats.topNMajorFaultProcesses.end(); ++it) {
+ if (it->count < stats.process.majorFaults) {
+ curUidProcessStats.topNMajorFaultProcesses
+ .emplace(it,
+ UidProcessStats::ProcessInfo{
+ .comm = stats.process.comm,
+ .count = stats.process.majorFaults,
+ });
+ curUidProcessStats.topNMajorFaultProcesses.pop_back();
+ break;
+ }
}
}
return uidProcessStats;
@@ -177,28 +220,42 @@
StringAppendF(&buffer,
"Percentage of change in major page faults since last collection: %.2f%%\n",
data.majorFaultsPercentChange);
- if (data.topNMajorFaults.size() > 0) {
+ if (data.topNMajorFaultUids.size() > 0) {
StringAppendF(&buffer, "\nTop N major page faults:\n%s\n", std::string(24, '-').c_str());
StringAppendF(&buffer,
"Android User ID, Package Name, Number of major page faults, "
"Percentage of total major page faults\n");
+ StringAppendF(&buffer,
+ "\tCommand, Number of major page faults, Percentage of UID's major page "
+ "faults\n");
}
- for (const auto& stat : data.topNMajorFaults) {
- StringAppendF(&buffer, "%" PRIu32 ", %s, %" PRIu64 ", %.2f%%\n", stat.userId,
- stat.packageName.c_str(), stat.count,
- percentage(stat.count, data.totalMajorFaults));
+ for (const auto& uidStats : data.topNMajorFaultUids) {
+ StringAppendF(&buffer, "%" PRIu32 ", %s, %" PRIu64 ", %.2f%%\n", uidStats.userId,
+ uidStats.packageName.c_str(), uidStats.count,
+ percentage(uidStats.count, data.totalMajorFaults));
+ for (const auto& procStats : uidStats.topNProcesses) {
+ StringAppendF(&buffer, "\t%s, %" PRIu64 ", %.2f%%\n", procStats.comm.c_str(),
+ procStats.count, percentage(procStats.count, uidStats.count));
+ }
}
if (data.topNIoBlockedUids.size() > 0) {
StringAppendF(&buffer, "\nTop N I/O waiting UIDs:\n%s\n", std::string(23, '-').c_str());
StringAppendF(&buffer,
"Android User ID, Package Name, Number of owned tasks waiting for I/O, "
"Percentage of owned tasks waiting for I/O\n");
+ StringAppendF(&buffer,
+ "\tCommand, Number of I/O waiting tasks, Percentage of UID's tasks waiting "
+ "for I/O\n");
}
for (size_t i = 0; i < data.topNIoBlockedUids.size(); ++i) {
- const auto& stat = data.topNIoBlockedUids[i];
- StringAppendF(&buffer, "%" PRIu32 ", %s, %" PRIu64 ", %.2f%%\n", stat.userId,
- stat.packageName.c_str(), stat.count,
- percentage(stat.count, data.topNIoBlockedUidsTotalTaskCnt[i]));
+ const auto& uidStats = data.topNIoBlockedUids[i];
+ StringAppendF(&buffer, "%" PRIu32 ", %s, %" PRIu64 ", %.2f%%\n", uidStats.userId,
+ uidStats.packageName.c_str(), uidStats.count,
+ percentage(uidStats.count, data.topNIoBlockedUidsTotalTaskCnt[i]));
+ for (const auto& procStats : uidStats.topNProcesses) {
+ StringAppendF(&buffer, "\t%s, %" PRIu64 ", %.2f%%\n", procStats.comm.c_str(),
+ procStats.count, percentage(procStats.count, uidStats.count));
+ }
}
return buffer;
}
@@ -663,21 +720,19 @@
for (auto it = topNReads.begin(); it != topNReads.end(); ++it) {
const UidIoUsage* curRead = *it;
- if (curRead->ios.sumReadBytes() > curUsage.ios.sumReadBytes()) {
- continue;
+ if (curRead->ios.sumReadBytes() < curUsage.ios.sumReadBytes()) {
+ topNReads.erase(topNReads.end() - 1);
+ topNReads.emplace(it, &curUsage);
+ break;
}
- topNReads.erase(topNReads.end() - 1);
- topNReads.emplace(it, &curUsage);
- break;
}
for (auto it = topNWrites.begin(); it != topNWrites.end(); ++it) {
const UidIoUsage* curWrite = *it;
- if (curWrite->ios.sumWriteBytes() > curUsage.ios.sumWriteBytes()) {
- continue;
+ if (curWrite->ios.sumWriteBytes() < curUsage.ios.sumWriteBytes()) {
+ topNWrites.erase(topNWrites.end() - 1);
+ topNWrites.emplace(it, &curUsage);
+ break;
}
- topNWrites.erase(topNWrites.end() - 1);
- topNWrites.emplace(it, &curUsage);
- break;
}
}
@@ -761,15 +816,14 @@
return Error() << "Failed to collect process stats: " << processStats.error();
}
- const auto& uidProcessStats = getUidProcessStats(*processStats);
-
+ const auto& uidProcessStats = getUidProcessStats(*processStats, mTopNStatsPerSubcategory);
std::unordered_set<uint32_t> unmappedUids;
// Fetch only the top N I/O blocked UIDs and UIDs with most major page faults.
UidProcessStats temp = {};
std::vector<const UidProcessStats*> topNIoBlockedUids(mTopNStatsPerCategory, &temp);
- std::vector<const UidProcessStats*> topNMajorFaults(mTopNStatsPerCategory, &temp);
+ std::vector<const UidProcessStats*> topNMajorFaultUids(mTopNStatsPerCategory, &temp);
processIoPerfData->totalMajorFaults = 0;
- for (const auto& it : uidProcessStats) {
+ for (const auto& it : *uidProcessStats) {
const UidProcessStats& curStats = it.second;
if (mUidToPackageNameMapping.find(curStats.uid) == mUidToPackageNameMapping.end()) {
unmappedUids.insert(curStats.uid);
@@ -777,21 +831,19 @@
processIoPerfData->totalMajorFaults += curStats.majorFaults;
for (auto it = topNIoBlockedUids.begin(); it != topNIoBlockedUids.end(); ++it) {
const UidProcessStats* topStats = *it;
- if (topStats->ioBlockedTasksCnt > curStats.ioBlockedTasksCnt) {
- continue;
+ if (topStats->ioBlockedTasksCnt < curStats.ioBlockedTasksCnt) {
+ topNIoBlockedUids.erase(topNIoBlockedUids.end() - 1);
+ topNIoBlockedUids.emplace(it, &curStats);
+ break;
}
- topNIoBlockedUids.erase(topNIoBlockedUids.end() - 1);
- topNIoBlockedUids.emplace(it, &curStats);
- break;
}
- for (auto it = topNMajorFaults.begin(); it != topNMajorFaults.end(); ++it) {
+ for (auto it = topNMajorFaultUids.begin(); it != topNMajorFaultUids.end(); ++it) {
const UidProcessStats* topStats = *it;
- if (topStats->majorFaults > curStats.majorFaults) {
- continue;
+ if (topStats->majorFaults < curStats.majorFaults) {
+ topNMajorFaultUids.erase(topNMajorFaultUids.end() - 1);
+ topNMajorFaultUids.emplace(it, &curStats);
+ break;
}
- topNMajorFaults.erase(topNMajorFaults.end() - 1);
- topNMajorFaults.emplace(it, &curStats);
- break;
}
}
@@ -807,7 +859,7 @@
// processes is < |ro.carwatchdog.top_n_stats_per_category|.
break;
}
- ProcessIoPerfData::Stats stats = {
+ ProcessIoPerfData::UidStats stats = {
.userId = multiuser_get_user_id(it->uid),
.packageName = std::to_string(it->uid),
.count = it->ioBlockedTasksCnt,
@@ -815,16 +867,23 @@
if (mUidToPackageNameMapping.find(it->uid) != mUidToPackageNameMapping.end()) {
stats.packageName = mUidToPackageNameMapping[it->uid];
}
+ for (const auto& pIt : it->topNIoBlockedProcesses) {
+ if (pIt.count == 0) {
+ break;
+ }
+ stats.topNProcesses.emplace_back(
+ ProcessIoPerfData::UidStats::ProcessStats{pIt.comm, pIt.count});
+ }
processIoPerfData->topNIoBlockedUids.emplace_back(stats);
processIoPerfData->topNIoBlockedUidsTotalTaskCnt.emplace_back(it->totalTasksCnt);
}
- for (const auto& it : topNMajorFaults) {
+ for (const auto& it : topNMajorFaultUids) {
if (it->majorFaults == 0) {
// End of non-zero elements. This case occurs when the number of UIDs with major faults
// is < |ro.carwatchdog.top_n_stats_per_category|.
break;
}
- ProcessIoPerfData::Stats stats = {
+ ProcessIoPerfData::UidStats stats = {
.userId = multiuser_get_user_id(it->uid),
.packageName = std::to_string(it->uid),
.count = it->majorFaults,
@@ -832,7 +891,14 @@
if (mUidToPackageNameMapping.find(it->uid) != mUidToPackageNameMapping.end()) {
stats.packageName = mUidToPackageNameMapping[it->uid];
}
- processIoPerfData->topNMajorFaults.emplace_back(stats);
+ for (const auto& pIt : it->topNMajorFaultProcesses) {
+ if (pIt.count == 0) {
+ break;
+ }
+ stats.topNProcesses.emplace_back(
+ ProcessIoPerfData::UidStats::ProcessStats{pIt.comm, pIt.count});
+ }
+ processIoPerfData->topNMajorFaultUids.emplace_back(stats);
}
if (mLastMajorFaults == 0) {
processIoPerfData->majorFaultsPercentChange = 0;
diff --git a/watchdog/server/src/IoPerfCollection.h b/watchdog/server/src/IoPerfCollection.h
index dc88114..4fef328 100644
--- a/watchdog/server/src/IoPerfCollection.h
+++ b/watchdog/server/src/IoPerfCollection.h
@@ -77,15 +77,20 @@
// Performance data collected from the `/proc/[pid]/stat` and `/proc/[pid]/task/[tid]/stat` files.
struct ProcessIoPerfData {
- struct Stats {
+ struct UidStats {
userid_t userId = 0;
std::string packageName;
uint64_t count = 0;
+ struct ProcessStats {
+ std::string comm = "";
+ uint64_t count = 0;
+ };
+ std::vector<ProcessStats> topNProcesses = {};
};
- std::vector<Stats> topNIoBlockedUids = {};
+ std::vector<UidStats> topNIoBlockedUids = {};
// Total # of tasks owned by each UID in |topNIoBlockedUids|.
std::vector<uint64_t> topNIoBlockedUidsTotalTaskCnt = {};
- std::vector<Stats> topNMajorFaults = {};
+ std::vector<UidStats> topNMajorFaultUids = {};
uint64_t totalMajorFaults = 0;
// Percentage of increase/decrease in the major page faults since last collection.
double majorFaultsPercentChange = 0.0;
diff --git a/watchdog/server/src/WatchdogProcessService.cpp b/watchdog/server/src/WatchdogProcessService.cpp
index 20f8fdf..749ed30 100644
--- a/watchdog/server/src/WatchdogProcessService.cpp
+++ b/watchdog/server/src/WatchdogProcessService.cpp
@@ -464,27 +464,32 @@
Result<void> WatchdogProcessService::dumpAndKillClientsIfNotResponding(TimeoutLength timeout) {
std::vector<int32_t> processIds;
+ std::vector<sp<ICarWatchdogClient>> clientsToNotify;
{
Mutex::Autolock lock(mMutex);
PingedClientMap& clients = mPingedClients[timeout];
for (PingedClientMap::const_iterator it = clients.cbegin(); it != clients.cend(); it++) {
pid_t pid = -1;
userid_t userId = -1;
- sp<IBinder> binder = BnCarWatchdog::asBinder(it->second.client);
+ sp<ICarWatchdogClient> client = it->second.client;
+ sp<IBinder> binder = BnCarWatchdog::asBinder(client);
std::vector<TimeoutLength> timeouts = {timeout};
- // Unhealthy clients are eventually removed from the list through binderDied when they
- // are killed.
findClientAndProcessLocked(timeouts, binder,
- [&](std::vector<ClientInfo>& /*clients*/,
+ [&](std::vector<ClientInfo>& clients,
std::vector<ClientInfo>::const_iterator it) {
pid = (*it).pid;
userId = (*it).userId;
+ clients.erase(it);
});
if (pid != -1 && mStoppedUserId.count(userId) == 0) {
+ clientsToNotify.push_back(client);
processIds.push_back(pid);
}
}
}
+ for (auto&& client : clientsToNotify) {
+ client->prepareProcessTermination();
+ }
return dumpAndKillAllProcesses(processIds);
}
diff --git a/watchdog/server/tests/IoPerfCollectionTest.cpp b/watchdog/server/tests/IoPerfCollectionTest.cpp
index fde6f2c..898447b 100644
--- a/watchdog/server/tests/IoPerfCollectionTest.cpp
+++ b/watchdog/server/tests/IoPerfCollectionTest.cpp
@@ -129,7 +129,9 @@
}
return isEqual;
};
- return std::equal(lhs.topNReads.begin(), lhs.topNReads.end(), rhs.topNReads.begin(), comp) &&
+ return lhs.topNReads.size() == rhs.topNReads.size() &&
+ std::equal(lhs.topNReads.begin(), lhs.topNReads.end(), rhs.topNReads.begin(), comp) &&
+ lhs.topNWrites.size() == rhs.topNWrites.size() &&
std::equal(lhs.topNWrites.begin(), lhs.topNWrites.end(), rhs.topNWrites.begin(), comp);
}
@@ -141,21 +143,32 @@
bool isEqual(const ProcessIoPerfData& lhs, const ProcessIoPerfData& rhs) {
if (lhs.topNIoBlockedUids.size() != rhs.topNIoBlockedUids.size() ||
- lhs.topNMajorFaults.size() != rhs.topNMajorFaults.size() ||
+ lhs.topNMajorFaultUids.size() != rhs.topNMajorFaultUids.size() ||
lhs.totalMajorFaults != rhs.totalMajorFaults ||
lhs.majorFaultsPercentChange != rhs.majorFaultsPercentChange) {
return false;
}
- auto comp = [&](const ProcessIoPerfData::Stats& l, const ProcessIoPerfData::Stats& r) -> bool {
- return l.userId == r.userId && l.packageName == r.packageName && l.count == r.count;
+ auto comp = [&](const ProcessIoPerfData::UidStats& l,
+ const ProcessIoPerfData::UidStats& r) -> bool {
+ auto comp = [&](const ProcessIoPerfData::UidStats::ProcessStats& l,
+ const ProcessIoPerfData::UidStats::ProcessStats& r) -> bool {
+ return l.comm == r.comm && l.count == r.count;
+ };
+ return l.userId == r.userId && l.packageName == r.packageName && l.count == r.count &&
+ l.topNProcesses.size() == r.topNProcesses.size() &&
+ std::equal(l.topNProcesses.begin(), l.topNProcesses.end(), r.topNProcesses.begin(),
+ comp);
};
- return std::equal(lhs.topNIoBlockedUids.begin(), lhs.topNIoBlockedUids.end(),
- rhs.topNIoBlockedUids.begin(), comp) &&
+ return lhs.topNIoBlockedUids.size() == lhs.topNIoBlockedUids.size() &&
+ std::equal(lhs.topNIoBlockedUids.begin(), lhs.topNIoBlockedUids.end(),
+ rhs.topNIoBlockedUids.begin(), comp) &&
+ lhs.topNIoBlockedUidsTotalTaskCnt.size() == rhs.topNIoBlockedUidsTotalTaskCnt.size() &&
std::equal(lhs.topNIoBlockedUidsTotalTaskCnt.begin(),
lhs.topNIoBlockedUidsTotalTaskCnt.end(),
rhs.topNIoBlockedUidsTotalTaskCnt.begin()) &&
- std::equal(lhs.topNMajorFaults.begin(), lhs.topNMajorFaults.end(),
- rhs.topNMajorFaults.begin(), comp);
+ lhs.topNMajorFaultUids.size() == rhs.topNMajorFaultUids.size() &&
+ std::equal(lhs.topNMajorFaultUids.begin(), lhs.topNMajorFaultUids.end(),
+ rhs.topNMajorFaultUids.begin(), comp);
}
bool isEqual(const IoPerfRecord& lhs, const IoPerfRecord& rhs) {
@@ -176,6 +189,9 @@
ASSERT_TRUE(sysprop::topNStatsPerCategory().has_value());
ASSERT_EQ(collector->mTopNStatsPerCategory, sysprop::topNStatsPerCategory().value());
+ ASSERT_TRUE(sysprop::topNStatsPerSubcategory().has_value());
+ ASSERT_EQ(collector->mTopNStatsPerSubcategory, sysprop::topNStatsPerSubcategory().value());
+
ASSERT_TRUE(sysprop::boottimeCollectionInterval().has_value());
ASSERT_EQ(std::chrono::duration_cast<std::chrono::seconds>(
collector->mBoottimeCollection.interval)
@@ -253,9 +269,9 @@
.totalCpuTime = 26900,
.ioBlockedProcessesCnt = 5,
.totalProcessesCnt = 22},
- .processIoPerfData = {.topNIoBlockedUids = {{0, "mount", 1}},
+ .processIoPerfData = {.topNIoBlockedUids = {{0, "mount", 1, {{"disk I/O", 1}}}},
.topNIoBlockedUidsTotalTaskCnt = {1},
- .topNMajorFaults = {{0, "mount", 5000}},
+ .topNMajorFaultUids = {{0, "mount", 5000, {{"disk I/O", 5000}}}},
.totalMajorFaults = 5000,
.majorFaultsPercentChange = 0.0},
};
@@ -312,11 +328,12 @@
.totalCpuTime = 19800,
.ioBlockedProcessesCnt = 6,
.totalProcessesCnt = 14},
- .processIoPerfData = {.topNIoBlockedUids = {{0, "mount", 2}},
- .topNIoBlockedUidsTotalTaskCnt = {2},
- .topNMajorFaults = {{0, "mount", 11000}},
- .totalMajorFaults = 11000,
- .majorFaultsPercentChange = ((11000.0 - 5000.0) / 5000.0) * 100},
+ .processIoPerfData =
+ {.topNIoBlockedUids = {{0, "mount", 2, {{"disk I/O", 2}}}},
+ .topNIoBlockedUidsTotalTaskCnt = {2},
+ .topNMajorFaultUids = {{0, "mount", 11000, {{"disk I/O", 11000}}}},
+ .totalMajorFaults = 11000,
+ .majorFaultsPercentChange = ((11000.0 - 5000.0) / 5000.0) * 100},
};
ret = looperStub->pollCache();
ASSERT_TRUE(ret) << ret.error().message();
@@ -374,9 +391,9 @@
.totalCpuTime = 21400,
.ioBlockedProcessesCnt = 8,
.totalProcessesCnt = 18},
- .processIoPerfData = {.topNIoBlockedUids = {{0, "mount", 2}},
+ .processIoPerfData = {.topNIoBlockedUids = {{0, "mount", 2, {{"disk I/O", 2}}}},
.topNIoBlockedUidsTotalTaskCnt = {2},
- .topNMajorFaults = {{0, "mount", 5000}},
+ .topNMajorFaultUids = {{0, "mount", 5000, {{"disk I/O", 5000}}}},
.totalMajorFaults = 5000,
.majorFaultsPercentChange = ((5000.0 - 11000.0) / 11000.0) * 100},
};
@@ -448,9 +465,9 @@
.totalCpuTime = 4276,
.ioBlockedProcessesCnt = 3,
.totalProcessesCnt = 15},
- .processIoPerfData = {.topNIoBlockedUids = {{0, "mount", 1}},
+ .processIoPerfData = {.topNIoBlockedUids = {{0, "mount", 1, {{"disk I/O", 1}}}},
.topNIoBlockedUidsTotalTaskCnt = {2},
- .topNMajorFaults = {{0, "mount", 4100}},
+ .topNMajorFaultUids = {{0, "mount", 4100, {{"disk I/O", 4100}}}},
.totalMajorFaults = 4100,
.majorFaultsPercentChange = ((4100.0 - 5000.0) / 5000.0) * 100},
};
@@ -508,11 +525,12 @@
.totalCpuTime = 43576,
.ioBlockedProcessesCnt = 4,
.totalProcessesCnt = 6},
- .processIoPerfData = {.topNIoBlockedUids = {{0, "mount", 2}},
- .topNIoBlockedUidsTotalTaskCnt = {2},
- .topNMajorFaults = {{0, "mount", 44300}},
- .totalMajorFaults = 44300,
- .majorFaultsPercentChange = ((44300.0 - 4100.0) / 4100.0) * 100},
+ .processIoPerfData =
+ {.topNIoBlockedUids = {{0, "mount", 2, {{"disk I/O", 2}}}},
+ .topNIoBlockedUidsTotalTaskCnt = {2},
+ .topNMajorFaultUids = {{0, "mount", 44300, {{"disk I/O", 44300}}}},
+ .totalMajorFaults = 44300,
+ .majorFaultsPercentChange = ((44300.0 - 4100.0) / 4100.0) * 100},
};
ret = looperStub->pollCache();
ASSERT_TRUE(ret) << ret.error().message();
@@ -587,12 +605,12 @@
.totalCpuTime = 47576,
.ioBlockedProcessesCnt = 13,
.totalProcessesCnt = 213},
- .processIoPerfData = {.topNIoBlockedUids = {{0, "mount", 2}},
- .topNIoBlockedUidsTotalTaskCnt = {2},
- .topNMajorFaults = {{0, "mount", 49800}},
- .totalMajorFaults = 49800,
- .majorFaultsPercentChange =
- ((49800.0 - 44300.0) / 44300.0) * 100},
+ .processIoPerfData =
+ {.topNIoBlockedUids = {{0, "mount", 2, {{"disk I/O", 2}}}},
+ .topNIoBlockedUidsTotalTaskCnt = {2},
+ .topNMajorFaultUids = {{0, "mount", 49800, {{"disk I/O", 49800}}}},
+ .totalMajorFaults = 49800,
+ .majorFaultsPercentChange = ((49800.0 - 44300.0) / 44300.0) * 100},
};
ret = looperStub->pollCache();
ASSERT_TRUE(ret) << ret.error().message();
@@ -646,12 +664,12 @@
.totalCpuTime = 48376,
.ioBlockedProcessesCnt = 57,
.totalProcessesCnt = 157},
- .processIoPerfData = {.topNIoBlockedUids = {{0, "mount", 2}},
- .topNIoBlockedUidsTotalTaskCnt = {2},
- .topNMajorFaults = {{0, "mount", 50900}},
- .totalMajorFaults = 50900,
- .majorFaultsPercentChange =
- ((50900.0 - 49800.0) / 49800.0) * 100},
+ .processIoPerfData =
+ {.topNIoBlockedUids = {{0, "mount", 2, {{"disk I/O", 2}}}},
+ .topNIoBlockedUidsTotalTaskCnt = {2},
+ .topNMajorFaultUids = {{0, "mount", 50900, {{"disk I/O", 50900}}}},
+ .totalMajorFaults = 50900,
+ .majorFaultsPercentChange = ((50900.0 - 49800.0) / 49800.0) * 100},
};
ret = looperStub->pollCache();
ASSERT_TRUE(ret) << ret.error().message();
@@ -729,9 +747,9 @@
.totalCpuTime = 20676,
.ioBlockedProcessesCnt = 1,
.totalProcessesCnt = 4},
- .processIoPerfData = {.topNIoBlockedUids = {{0, "mount", 2}},
+ .processIoPerfData = {.topNIoBlockedUids = {{0, "mount", 2, {{"disk I/O", 2}}}},
.topNIoBlockedUidsTotalTaskCnt = {2},
- .topNMajorFaults = {{0, "mount", 5701}},
+ .topNMajorFaultUids = {{0, "mount", 5701, {{"disk I/O", 5701}}}},
.totalMajorFaults = 5701,
.majorFaultsPercentChange = ((5701.0 - 50900.0) / 50900.0) * 100},
};
@@ -1153,6 +1171,7 @@
.userId = 10,
.packageName = "shared:android.uid.system",
.count = 4,
+ .topNProcesses = {{"logd", 3}, {"system_server", 1}},
});
expectedProcessIoPerfData.topNIoBlockedUidsTotalTaskCnt.push_back(6);
expectedProcessIoPerfData.topNIoBlockedUids.push_back({
@@ -1160,19 +1179,22 @@
.userId = 0,
.packageName = "mount",
.count = 3,
+ .topNProcesses = {{"disk I/O", 3}},
});
expectedProcessIoPerfData.topNIoBlockedUidsTotalTaskCnt.push_back(3);
- expectedProcessIoPerfData.topNMajorFaults.push_back({
+ expectedProcessIoPerfData.topNMajorFaultUids.push_back({
// uid: 1001234
.userId = 10,
.packageName = "1001234",
.count = 89765,
+ .topNProcesses = {{"tombstoned", 89765}},
});
- expectedProcessIoPerfData.topNMajorFaults.push_back({
+ expectedProcessIoPerfData.topNMajorFaultUids.push_back({
// uid: 1009
.userId = 0,
.packageName = "mount",
.count = 45678,
+ .topNProcesses = {{"disk I/O", 45678}},
});
expectedProcessIoPerfData.totalMajorFaults = 156663;
expectedProcessIoPerfData.majorFaultsPercentChange = 0;
@@ -1185,6 +1207,7 @@
IoPerfCollection collector;
collector.mProcPidStat = new ProcPidStat(firstSnapshot.path);
collector.mTopNStatsPerCategory = 2;
+ collector.mTopNStatsPerSubcategory = 2;
ASSERT_TRUE(collector.mProcPidStat->enabled())
<< "Files under the temporary proc directory are inaccessible";
@@ -1221,19 +1244,22 @@
.userId = 10,
.packageName = "shared:android.uid.system",
.count = 1,
+ .topNProcesses = {{"system_server", 1}},
});
expectedProcessIoPerfData.topNIoBlockedUidsTotalTaskCnt.push_back(3);
- expectedProcessIoPerfData.topNMajorFaults.push_back({
+ expectedProcessIoPerfData.topNMajorFaultUids.push_back({
// uid: 1001000
.userId = 10,
.packageName = "shared:android.uid.system",
.count = 12000,
+ .topNProcesses = {{"system_server", 12000}},
});
- expectedProcessIoPerfData.topNMajorFaults.push_back({
+ expectedProcessIoPerfData.topNMajorFaultUids.push_back({
// uid: 0
.userId = 0,
.packageName = "root",
.count = 660,
+ .topNProcesses = {{"init", 660}},
});
expectedProcessIoPerfData.totalMajorFaults = 12660;
expectedProcessIoPerfData.majorFaultsPercentChange = ((12660.0 - 156663.0) / 156663.0) * 100;
@@ -1269,11 +1295,12 @@
{453, "453 (init) S 0 0 0 0 0 0 0 0 80 0 0 0 0 0 0 0 2 0 275\n"},
};
struct ProcessIoPerfData expectedProcessIoPerfData = {};
- expectedProcessIoPerfData.topNMajorFaults.push_back({
+ expectedProcessIoPerfData.topNMajorFaultUids.push_back({
// uid: 0
.userId = 0,
.packageName = "root",
.count = 880,
+ .topNProcesses = {{"init", 880}},
});
expectedProcessIoPerfData.totalMajorFaults = 880;
expectedProcessIoPerfData.majorFaultsPercentChange = 0.0;
@@ -1285,6 +1312,7 @@
IoPerfCollection collector;
collector.mTopNStatsPerCategory = 5;
+ collector.mTopNStatsPerSubcategory = 3;
collector.mProcPidStat = new ProcPidStat(prodDir.path);
struct ProcessIoPerfData actualProcessIoPerfData = {};
ret = collector.collectProcessIoPerfDataLocked(&actualProcessIoPerfData);
diff --git a/watchdog/server/tests/WatchdogBinderMediatorTest.cpp b/watchdog/server/tests/WatchdogBinderMediatorTest.cpp
index 2342c27..0d1cf05 100644
--- a/watchdog/server/tests/WatchdogBinderMediatorTest.cpp
+++ b/watchdog/server/tests/WatchdogBinderMediatorTest.cpp
@@ -75,6 +75,7 @@
class MockICarWatchdogClient : public ICarWatchdogClient {
public:
MOCK_METHOD(Status, checkIfAlive, (int32_t sessionId, TimeoutLength timeout), (override));
+ MOCK_METHOD(Status, prepareProcessTermination, (), (override));
MOCK_METHOD(IBinder*, onAsBinder, (), (override));
MOCK_METHOD(int32_t, getInterfaceVersion, (), (override));
MOCK_METHOD(std::string, getInterfaceHash, (), (override));
diff --git a/watchdog/testclient/src/WatchdogClient.cpp b/watchdog/testclient/src/WatchdogClient.cpp
index 41a2b26..4c381c6 100644
--- a/watchdog/testclient/src/WatchdogClient.cpp
+++ b/watchdog/testclient/src/WatchdogClient.cpp
@@ -59,6 +59,11 @@
return ndk::ScopedAStatus::ok();
}
+ndk::ScopedAStatus WatchdogClient::prepareProcessTermination() {
+ ALOGI("This process is being terminated by car watchdog");
+ return ndk::ScopedAStatus::ok();
+}
+
bool WatchdogClient::initialize(const CommandParam& param) {
ndk::SpAIBinder binder(
AServiceManager_getService("android.automotive.watchdog.ICarWatchdog/default"));
diff --git a/watchdog/testclient/src/WatchdogClient.h b/watchdog/testclient/src/WatchdogClient.h
index 95f11ca..3174f98 100644
--- a/watchdog/testclient/src/WatchdogClient.h
+++ b/watchdog/testclient/src/WatchdogClient.h
@@ -51,6 +51,7 @@
explicit WatchdogClient(const ::android::sp<::android::Looper>& handlerLooper);
ndk::ScopedAStatus checkIfAlive(int32_t sessionId, TimeoutLength timeout) override;
+ ndk::ScopedAStatus prepareProcessTermination() override;
bool initialize(const CommandParam& param);
void finalize();