Merge "CarService detects recurring overuse" into sc-v2-dev
diff --git a/cpp/watchdog/aidl/android/automotive/watchdog/internal/UserState.aidl b/cpp/watchdog/aidl/android/automotive/watchdog/internal/UserState.aidl
index 515fb41..95461eb 100644
--- a/cpp/watchdog/aidl/android/automotive/watchdog/internal/UserState.aidl
+++ b/cpp/watchdog/aidl/android/automotive/watchdog/internal/UserState.aidl
@@ -32,6 +32,11 @@
USER_STATE_STOPPED,
/**
+ * The user is removed.
+ */
+ USER_STATE_REMOVED,
+
+ /**
* Number of available user states.
*/
NUM_USER_STATES,
diff --git a/cpp/watchdog/server/src/IoOveruseMonitor.cpp b/cpp/watchdog/server/src/IoOveruseMonitor.cpp
index 0ebe0d4..edf30d8 100644
--- a/cpp/watchdog/server/src/IoOveruseMonitor.cpp
+++ b/cpp/watchdog/server/src/IoOveruseMonitor.cpp
@@ -23,11 +23,11 @@
#include <WatchdogProperties.sysprop.h>
#include <android-base/file.h>
+#include <android-base/strings.h>
#include <android/automotive/watchdog/internal/PackageIdentifier.h>
#include <android/automotive/watchdog/internal/UidType.h>
#include <binder/IPCThreadState.h>
#include <binder/Status.h>
-#include <cutils/multiuser.h>
#include <log/log.h>
#include <processgroup/sched_policy.h>
@@ -53,8 +53,10 @@
using ::android::automotive::watchdog::internal::ResourceOveruseConfiguration;
using ::android::automotive::watchdog::internal::UidType;
using ::android::automotive::watchdog::internal::UserPackageIoUsageStats;
+using ::android::base::EndsWith;
using ::android::base::Error;
using ::android::base::Result;
+using ::android::base::StringPrintf;
using ::android::base::WriteStringToFd;
using ::android::binder::Status;
@@ -598,6 +600,7 @@
std::unordered_set<std::string> uniquePackageNames;
std::copy(packageNames.begin(), packageNames.end(),
std::inserter(uniquePackageNames, uniquePackageNames.end()));
+ std::unique_lock writeLock(mRwMutex);
for (auto& [key, usage] : mUserPackageDailyIoUsageById) {
if (uniquePackageNames.find(usage.packageInfo.packageIdentifier.name) !=
uniquePackageNames.end()) {
@@ -607,6 +610,37 @@
return {};
}
+void IoOveruseMonitor::removeStatsForUser(userid_t userId) {
+ std::unique_lock writeLock(mRwMutex);
+ for (auto it = mUserPackageDailyIoUsageById.begin();
+ it != mUserPackageDailyIoUsageById.end();) {
+ if (multiuser_get_user_id(it->second.packageInfo.packageIdentifier.uid) == userId) {
+ it = mUserPackageDailyIoUsageById.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ // |mPrevBootIoUsageStatsById| keys are constructed using |uniquePackageIdStr| method. Thus, the
+ // key suffix would contain the userId. The value in this map is |IoUsageStats|, which doesn't
+ // contain the userId, so this is the only way to delete cached previous boot stats for
+ // the removed user.
+ std::string keySuffix = StringPrintf(":%" PRId32, userId);
+ for (auto it = mPrevBootIoUsageStatsById.begin(); it != mPrevBootIoUsageStatsById.end();) {
+ if (EndsWith(it->first, keySuffix)) {
+ it = mPrevBootIoUsageStatsById.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ for (auto it = mLatestIoOveruseStats.begin(); it != mLatestIoOveruseStats.end();) {
+ if (multiuser_get_user_id(it->uid) == userId) {
+ it = mLatestIoOveruseStats.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
void IoOveruseMonitor::handleBinderDeath(const wp<IBinder>& who) {
std::unique_lock writeLock(mRwMutex);
IBinder* binder = who.unsafe_get();
diff --git a/cpp/watchdog/server/src/IoOveruseMonitor.h b/cpp/watchdog/server/src/IoOveruseMonitor.h
index 5fa91aa..d6c2616 100644
--- a/cpp/watchdog/server/src/IoOveruseMonitor.h
+++ b/cpp/watchdog/server/src/IoOveruseMonitor.h
@@ -31,6 +31,7 @@
#include <android/automotive/watchdog/internal/IoOveruseConfiguration.h>
#include <android/automotive/watchdog/internal/PackageInfo.h>
#include <android/automotive/watchdog/internal/PackageIoOveruseStats.h>
+#include <cutils/multiuser.h>
#include <utils/Mutex.h>
#include <time.h>
@@ -92,6 +93,9 @@
virtual android::base::Result<void> resetIoOveruseStats(
const std::vector<std::string>& packageNames) = 0;
+
+ // Removes stats for the given user from the internal cache.
+ virtual void removeStatsForUser(userid_t userId) = 0;
};
class IoOveruseMonitor final : public IIoOveruseMonitor {
@@ -161,6 +165,8 @@
android::base::Result<void> resetIoOveruseStats(
const std::vector<std::string>& packageName) override;
+ void removeStatsForUser(userid_t userId) override;
+
protected:
android::base::Result<void> init();
diff --git a/cpp/watchdog/server/src/WatchdogInternalHandler.cpp b/cpp/watchdog/server/src/WatchdogInternalHandler.cpp
index 2441ccf..d28b322 100644
--- a/cpp/watchdog/server/src/WatchdogInternalHandler.cpp
+++ b/cpp/watchdog/server/src/WatchdogInternalHandler.cpp
@@ -186,7 +186,7 @@
return fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT,
StringPrintf("Invalid user state %d", userState));
}
- return mWatchdogProcessService->notifyUserStateChange(userId, userState);
+ return handleUserStateChange(userId, userState);
}
case aawi::StateType::BOOT_PHASE: {
aawi::BootPhase phase = static_cast<aawi::BootPhase>(static_cast<uint32_t>(arg1));
@@ -225,6 +225,29 @@
return Status::ok();
}
+Status WatchdogInternalHandler::handleUserStateChange(userid_t userId, aawi::UserState userState) {
+ std::string stateDesc;
+ switch (userState) {
+ case aawi::UserState::USER_STATE_STARTED:
+ stateDesc = "started";
+ mWatchdogProcessService->notifyUserStateChange(userId, /*isStarted=*/true);
+ break;
+ case aawi::UserState::USER_STATE_STOPPED:
+ stateDesc = "stopped";
+ mWatchdogProcessService->notifyUserStateChange(userId, /*isStarted=*/false);
+ break;
+ case aawi::UserState::USER_STATE_REMOVED:
+ stateDesc = "removed";
+ mIoOveruseMonitor->removeStatsForUser(userId);
+ break;
+ default:
+ ALOGW("Unsupported user state: %d", userState);
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Unsupported user state");
+ }
+ ALOGI("Received user state change: user(%" PRId32 ") is %s", userId, stateDesc.c_str());
+ return Status::ok();
+}
+
Status WatchdogInternalHandler::updateResourceOveruseConfigurations(
const std::vector<ResourceOveruseConfiguration>& configs) {
Status status = checkSystemUser();
diff --git a/cpp/watchdog/server/src/WatchdogInternalHandler.h b/cpp/watchdog/server/src/WatchdogInternalHandler.h
index 1000c8e..6634d5b 100644
--- a/cpp/watchdog/server/src/WatchdogInternalHandler.h
+++ b/cpp/watchdog/server/src/WatchdogInternalHandler.h
@@ -105,6 +105,9 @@
android::binder::Status handlePowerCycleChange(
android::automotive::watchdog::internal::PowerCycle powerCycle);
+ android::binder::Status handleUserStateChange(
+ userid_t userId, android::automotive::watchdog::internal::UserState userState);
+
android::sp<WatchdogBinderMediator> mBinderMediator;
android::sp<IWatchdogServiceHelper> mWatchdogServiceHelper;
android::sp<WatchdogProcessService> mWatchdogProcessService;
diff --git a/cpp/watchdog/server/src/WatchdogProcessService.cpp b/cpp/watchdog/server/src/WatchdogProcessService.cpp
index 891c6e2..3eca5ec 100644
--- a/cpp/watchdog/server/src/WatchdogProcessService.cpp
+++ b/cpp/watchdog/server/src/WatchdogProcessService.cpp
@@ -289,24 +289,14 @@
}
}
-Status WatchdogProcessService::notifyUserStateChange(userid_t userId, aawi::UserState state) {
+void WatchdogProcessService::notifyUserStateChange(userid_t userId, bool isStarted) {
std::string buffer;
Mutex::Autolock lock(mMutex);
- switch (state) {
- case aawi::UserState::USER_STATE_STARTED:
- mStoppedUserIds.erase(userId);
- buffer = StringPrintf("user(%d) is started", userId);
- break;
- case aawi::UserState::USER_STATE_STOPPED:
- mStoppedUserIds.insert(userId);
- buffer = StringPrintf("user(%d) is stopped", userId);
- break;
- default:
- ALOGW("Unsupported user state: %d", state);
- return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Unsupported user state");
+ if (isStarted) {
+ mStoppedUserIds.erase(userId);
+ } else {
+ mStoppedUserIds.insert(userId);
}
- ALOGI("Received user state change: %s", buffer.c_str());
- return Status::ok();
}
Result<void> WatchdogProcessService::dump(int fd, const Vector<String16>& /*args*/) {
diff --git a/cpp/watchdog/server/src/WatchdogProcessService.h b/cpp/watchdog/server/src/WatchdogProcessService.h
index 46d3ef4..d66a420 100644
--- a/cpp/watchdog/server/src/WatchdogProcessService.h
+++ b/cpp/watchdog/server/src/WatchdogProcessService.h
@@ -80,8 +80,7 @@
monitor,
int32_t pid);
virtual void setEnabled(bool isEnabled);
- virtual android::binder::Status notifyUserStateChange(
- userid_t userId, android::automotive::watchdog::internal::UserState state);
+ virtual void notifyUserStateChange(userid_t userId, bool isStarted);
private:
enum ClientType {
diff --git a/cpp/watchdog/server/tests/IoOveruseMonitorTest.cpp b/cpp/watchdog/server/tests/IoOveruseMonitorTest.cpp
index c1dd4f7..752be74 100644
--- a/cpp/watchdog/server/tests/IoOveruseMonitorTest.cpp
+++ b/cpp/watchdog/server/tests/IoOveruseMonitorTest.cpp
@@ -1064,6 +1064,85 @@
ASSERT_FALSE(mIoOveruseMonitor->updateResourceOveruseConfigurations({}).ok());
}
+TEST_F(IoOveruseMonitorTest, TestRemoveUser) {
+ std::vector<UserPackageIoUsageStats> todayIoUsageStats =
+ {constructUserPackageIoUsageStats(
+ /*userId=*/11, "com.android.google.package",
+ /*writtenBytes=*/constructPerStateBytes(100'000, 85'000, 120'000),
+ /*forgivenWriteBytes=*/constructPerStateBytes(70'000, 60'000, 100'000),
+ /*totalOveruses=*/3),
+ constructUserPackageIoUsageStats(
+ /*userId=*/12, "com.android.kitchensink",
+ /*writtenBytes=*/constructPerStateBytes(50'000, 40'000, 35'000),
+ /*forgivenWriteBytes=*/constructPerStateBytes(30'000, 30'000, 30'000),
+ /*totalOveruses=*/6)};
+ EXPECT_CALL(*mMockWatchdogServiceHelper, getTodayIoUsageStats(_))
+ .WillOnce(DoAll(SetArgPointee<0>(todayIoUsageStats), Return(Status::ok())));
+
+ EXPECT_CALL(*mMockUidStatsCollector, deltaStats())
+ .WillOnce(Return(
+ constructUidStats({{1001000, {/*fgWrBytes=*/70'000, /*bgWrBytes=*/20'000}},
+ {1112345, {/*fgWrBytes=*/35'000, /*bgWrBytes=*/15'000}}})));
+
+ std::vector<PackageIoOveruseStats> actualIoOveruseStats;
+ EXPECT_CALL(*mMockWatchdogServiceHelper, latestIoOveruseStats(_))
+ .WillOnce(DoAll(SaveArg<0>(&actualIoOveruseStats), Return(Status::ok())));
+
+ time_t currentTime = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
+ const auto [startTime, durationInSeconds] = calculateStartAndDuration(currentTime);
+
+ ASSERT_RESULT_OK(mIoOveruseMonitor->onPeriodicCollection(currentTime, SystemState::NORMAL_MODE,
+ mMockUidStatsCollector, nullptr));
+
+ std::vector<PackageIoOveruseStats> expectedIoOveruseStats =
+ {constructPackageIoOveruseStats(
+ /*uid*=*/1001000, /*shouldNotify=*/false, /*isKillable=*/false,
+ /*remaining=*/constructPerStateBytes(10'000, 20'000, 100'000),
+ /*written=*/constructPerStateBytes(70'000, 20'000, 0),
+ /*forgiven=*/constructPerStateBytes(0, 0, 0),
+ /*totalOveruses=*/0, startTime, durationInSeconds),
+ constructPackageIoOveruseStats(
+ /*uid*=*/1112345, /*shouldNotify=*/true, /*isKillable=*/true,
+ /*remaining=*/constructPerStateBytes(5'000, 0, 80'000),
+ /*written=*/constructPerStateBytes(135'000, 100'000, 120'000),
+ /*forgiven=*/constructPerStateBytes(70'000, 90'000, 100'000),
+ /*totalOveruses=*/4, startTime, durationInSeconds)};
+ EXPECT_THAT(actualIoOveruseStats, UnorderedElementsAreArray(expectedIoOveruseStats))
+ << "Expected: " << toString(expectedIoOveruseStats)
+ << "\nActual: " << toString(actualIoOveruseStats);
+
+ mIoOveruseMonitor->removeStatsForUser(/*userId=*/11);
+ mIoOveruseMonitor->removeStatsForUser(/*userId=*/12);
+
+ EXPECT_CALL(*mMockUidStatsCollector, deltaStats())
+ .WillOnce(Return(
+ constructUidStats({{1112345, {/*fgWrBytes=*/70'000, /*bgWrBytes=*/40'000}},
+ {1245678, {/*fgWrBytes=*/30'000, /*bgWrBytes=*/10'000}}})));
+
+ actualIoOveruseStats.clear();
+ EXPECT_CALL(*mMockWatchdogServiceHelper, latestIoOveruseStats(_))
+ .WillOnce(DoAll(SaveArg<0>(&actualIoOveruseStats), Return(Status::ok())));
+
+ ASSERT_RESULT_OK(mIoOveruseMonitor->onPeriodicCollection(currentTime, SystemState::GARAGE_MODE,
+ mMockUidStatsCollector, nullptr));
+
+ expectedIoOveruseStats = {constructPackageIoOveruseStats(
+ /*uid*=*/1112345, /*shouldNotify=*/true, /*isKillable=*/true,
+ /*remaining=*/constructPerStateBytes(70'000, 30'000, 0),
+ /*written=*/constructPerStateBytes(0, 0, 110'000),
+ /*forgiven=*/constructPerStateBytes(0, 0, 100'000),
+ /*totalOveruses=*/1, startTime, durationInSeconds),
+ constructPackageIoOveruseStats(
+ /*uid*=*/1245678, /*shouldNotify=*/true, /*isKillable=*/true,
+ /*remaining=*/constructPerStateBytes(30'000, 15'000, 0),
+ /*written=*/constructPerStateBytes(0, 0, 40'000),
+ /*forgiven=*/constructPerStateBytes(0, 0, 40'000),
+ /*totalOveruses=*/4, startTime, durationInSeconds)};
+ EXPECT_THAT(actualIoOveruseStats, UnorderedElementsAreArray(expectedIoOveruseStats))
+ << "Expected: " << toString(expectedIoOveruseStats)
+ << "\nActual: " << toString(actualIoOveruseStats);
+}
+
} // namespace watchdog
} // namespace automotive
} // namespace android
diff --git a/cpp/watchdog/server/tests/MockIoOveruseMonitor.h b/cpp/watchdog/server/tests/MockIoOveruseMonitor.h
index 294276b..0e3f4c4 100644
--- a/cpp/watchdog/server/tests/MockIoOveruseMonitor.h
+++ b/cpp/watchdog/server/tests/MockIoOveruseMonitor.h
@@ -53,6 +53,7 @@
(const, override));
MOCK_METHOD(android::base::Result<void>, resetIoOveruseStats, (const std::vector<std::string>&),
(override));
+ MOCK_METHOD(void, removeStatsForUser, (userid_t), (override));
};
} // namespace watchdog
diff --git a/cpp/watchdog/server/tests/MockWatchdogProcessService.h b/cpp/watchdog/server/tests/MockWatchdogProcessService.h
index 44f3377..304a2a4 100644
--- a/cpp/watchdog/server/tests/MockWatchdogProcessService.h
+++ b/cpp/watchdog/server/tests/MockWatchdogProcessService.h
@@ -40,42 +40,37 @@
class MockWatchdogProcessService : public WatchdogProcessService {
public:
MockWatchdogProcessService() : WatchdogProcessService(nullptr) {}
- MOCK_METHOD(android::base::Result<void>, dump, (int fd, const Vector<android::String16>& args),
+ MOCK_METHOD(android::base::Result<void>, dump, (int fd, const Vector<android::String16>&),
(override));
MOCK_METHOD(android::base::Result<void>, registerWatchdogServiceHelper,
- (const android::sp<IWatchdogServiceHelper>& helper), (override));
+ (const android::sp<IWatchdogServiceHelper>&), (override));
MOCK_METHOD(android::binder::Status, registerClient,
- (const sp<ICarWatchdogClient>& client, TimeoutLength timeout), (override));
- MOCK_METHOD(android::binder::Status, unregisterClient, (const sp<ICarWatchdogClient>& client),
+ (const sp<ICarWatchdogClient>&, TimeoutLength), (override));
+ MOCK_METHOD(android::binder::Status, unregisterClient, (const sp<ICarWatchdogClient>&),
(override));
- MOCK_METHOD(android::binder::Status, registerCarWatchdogService,
- (const android::sp<IBinder>& binder), (override));
- MOCK_METHOD(void, unregisterCarWatchdogService, (const android::sp<IBinder>& binder),
+ MOCK_METHOD(android::binder::Status, registerCarWatchdogService, (const android::sp<IBinder>&),
(override));
+ MOCK_METHOD(void, unregisterCarWatchdogService, (const android::sp<IBinder>&), (override));
MOCK_METHOD(android::binder::Status, registerMonitor,
- (const sp<android::automotive::watchdog::internal::ICarWatchdogMonitor>& monitor),
+ (const sp<android::automotive::watchdog::internal::ICarWatchdogMonitor>&),
(override));
MOCK_METHOD(android::binder::Status, unregisterMonitor,
- (const sp<android::automotive::watchdog::internal::ICarWatchdogMonitor>& monitor),
+ (const sp<android::automotive::watchdog::internal::ICarWatchdogMonitor>&),
(override));
- MOCK_METHOD(android::binder::Status, tellClientAlive,
- (const sp<ICarWatchdogClient>& client, int32_t sessionId), (override));
+ MOCK_METHOD(android::binder::Status, tellClientAlive, (const sp<ICarWatchdogClient>&, int32_t),
+ (override));
MOCK_METHOD(android::binder::Status, tellCarWatchdogServiceAlive,
(const android::sp<
- android::automotive::watchdog::internal::ICarWatchdogServiceForSystem>&
- service,
- const std::vector<int32_t>& clientsNotResponding, int32_t sessionId),
+ android::automotive::watchdog::internal::ICarWatchdogServiceForSystem>&,
+ const std::vector<int32_t>&, int32_t),
(override));
MOCK_METHOD(android::binder::Status, tellDumpFinished,
- (const android::sp<android::automotive::watchdog::internal::ICarWatchdogMonitor>&
- monitor,
- int32_t pid),
+ (const android::sp<android::automotive::watchdog::internal::ICarWatchdogMonitor>&,
+ int32_t),
(override));
MOCK_METHOD(void, setEnabled, (bool), (override));
- MOCK_METHOD(android::binder::Status, notifyUserStateChange,
- (userid_t userId, android::automotive::watchdog::internal::UserState state),
- (override));
+ MOCK_METHOD(void, notifyUserStateChange, (userid_t, bool), (override));
};
} // namespace watchdog
diff --git a/cpp/watchdog/server/tests/WatchdogInternalHandlerTest.cpp b/cpp/watchdog/server/tests/WatchdogInternalHandlerTest.cpp
index a5abbc4..3d95bc6 100644
--- a/cpp/watchdog/server/tests/WatchdogInternalHandlerTest.cpp
+++ b/cpp/watchdog/server/tests/WatchdogInternalHandlerTest.cpp
@@ -362,12 +362,21 @@
ASSERT_TRUE(status.isOk()) << status;
}
-TEST_F(WatchdogInternalHandlerTest, TestNotifyUserStateChange) {
+TEST_F(WatchdogInternalHandlerTest, TestNotifyUserStateChangeWithStartedUser) {
setSystemCallingUid();
aawi::StateType type = aawi::StateType::USER_STATE;
- EXPECT_CALL(*mMockWatchdogProcessService,
- notifyUserStateChange(234567, aawi::UserState::USER_STATE_STOPPED))
- .WillOnce(Return(Status::ok()));
+ EXPECT_CALL(*mMockWatchdogProcessService, notifyUserStateChange(234567, /*isStarted=*/true));
+ Status status = mWatchdogInternalHandler
+ ->notifySystemStateChange(type, 234567,
+ static_cast<int32_t>(
+ aawi::UserState::USER_STATE_STARTED));
+ ASSERT_TRUE(status.isOk()) << status;
+}
+
+TEST_F(WatchdogInternalHandlerTest, TestNotifyUserStateChangeWithStoppedUser) {
+ setSystemCallingUid();
+ aawi::StateType type = aawi::StateType::USER_STATE;
+ EXPECT_CALL(*mMockWatchdogProcessService, notifyUserStateChange(234567, /*isStarted=*/false));
Status status = mWatchdogInternalHandler
->notifySystemStateChange(type, 234567,
static_cast<int32_t>(
@@ -375,6 +384,17 @@
ASSERT_TRUE(status.isOk()) << status;
}
+TEST_F(WatchdogInternalHandlerTest, TestNotifyUserStateChangeWithRemovedUser) {
+ setSystemCallingUid();
+ aawi::StateType type = aawi::StateType::USER_STATE;
+ EXPECT_CALL(*mMockIoOveruseMonitor, removeStatsForUser(/*userId=*/234567));
+ Status status = mWatchdogInternalHandler
+ ->notifySystemStateChange(type, 234567,
+ static_cast<int32_t>(
+ aawi::UserState::USER_STATE_REMOVED));
+ ASSERT_TRUE(status.isOk()) << status;
+}
+
TEST_F(WatchdogInternalHandlerTest, TestErrorOnNotifyUserStateChangeWithInvalidArgs) {
EXPECT_CALL(*mMockWatchdogProcessService, notifyUserStateChange(_, _)).Times(0);
aawi::StateType type = aawi::StateType::USER_STATE;
diff --git a/service/src/com/android/car/watchdog/CarWatchdogService.java b/service/src/com/android/car/watchdog/CarWatchdogService.java
index eab0e4b..ae9897d 100644
--- a/service/src/com/android/car/watchdog/CarWatchdogService.java
+++ b/service/src/com/android/car/watchdog/CarWatchdogService.java
@@ -91,6 +91,8 @@
"com.android.car.watchdog.ACTION_DISMISS_RESOURCE_OVERUSE_NOTIFICATION";
static final String ACTION_RESOURCE_OVERUSE_DISABLE_APP =
"com.android.car.watchdog.ACTION_RESOURCE_OVERUSE_DISABLE_APP";
+
+ @VisibleForTesting
static final int MISSING_ARG_VALUE = -1;
private static final String FALLBACK_DATA_SYSTEM_CAR_DIR_PATH = "/data/system/car";
@@ -124,7 +126,7 @@
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
+ String action = intent.getAction();
switch (action) {
case ACTION_DISMISS_RESOURCE_OVERUSE_NOTIFICATION:
case ACTION_LAUNCH_APP_SETTINGS:
@@ -145,8 +147,20 @@
notifyGarageModeChange(garageMode);
return;
case ACTION_USER_REMOVED:
- UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
- mWatchdogPerfHandler.deleteUser(user.getIdentifier());
+ UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER);
+ int userId = userHandle.getIdentifier();
+ try {
+ mCarWatchdogDaemonHelper.notifySystemStateChange(StateType.USER_STATE,
+ userId, UserState.USER_STATE_REMOVED);
+ if (DEBUG) {
+ Slogf.d(TAG, "Notified car watchdog daemon of removed user %d",
+ userId);
+ }
+ } catch (RemoteException e) {
+ Slogf.w(TAG, e, "Failed to notify car watchdog daemon of removed user %d",
+ userId);
+ }
+ mWatchdogPerfHandler.deleteUser(userId);
return;
}
}
@@ -482,7 +496,9 @@
Slogf.d(TAG, "Notified car watchdog daemon of user states");
}
} catch (RemoteException | RuntimeException e) {
- Slogf.w(TAG, "Notifying latest user states failed: %s", e);
+ // When car watchdog daemon is not connected, the {@link mCarWatchdogDaemonHelper}
+ // throws IllegalStateException. Catch the exception to avoid crashing the process.
+ Slogf.w(TAG, e, "Notifying latest user states failed");
}
}
@@ -498,6 +514,8 @@
Slogf.d(TAG, "Notified car watchdog daemon of power cycle(%d)", powerCycle);
}
} catch (RemoteException | RuntimeException e) {
+ // When car watchdog daemon is not connected, the {@link mCarWatchdogDaemonHelper}
+ // throws IllegalStateException. Catch the exception to avoid crashing the process.
Slogf.w(TAG, e, "Notifying power cycle change to %d failed", powerCycle);
}
}
@@ -510,6 +528,8 @@
Slogf.d(TAG, "Notified car watchdog daemon of garage mode(%d)", garageMode);
}
} catch (RemoteException | RuntimeException e) {
+ // When car watchdog daemon is not connected, the {@link mCarWatchdogDaemonHelper}
+ // throws IllegalStateException. Catch the exception to avoid crashing the process.
Slogf.w(TAG, e, "Notifying garage mode change to %d failed", garageMode);
}
}
@@ -535,7 +555,9 @@
Slogf.d(TAG, "CarWatchdogService registers to car watchdog daemon");
}
} catch (RemoteException | RuntimeException e) {
- Slogf.w(TAG, "Cannot register to car watchdog daemon: %s", e);
+ // When car watchdog daemon is not connected, the {@link mCarWatchdogDaemonHelper}
+ // throws IllegalStateException. Catch the exception to avoid crashing the process.
+ Slogf.w(TAG, e, "Cannot register to car watchdog daemon");
}
notifyAllUserStates();
CarPowerManagementService powerService =
@@ -567,7 +589,9 @@
Slogf.d(TAG, "CarWatchdogService unregisters from car watchdog daemon");
}
} catch (RemoteException | RuntimeException e) {
- Slogf.w(TAG, "Cannot unregister from car watchdog daemon: %s", e);
+ // When car watchdog daemon is not connected, the {@link mCarWatchdogDaemonHelper}
+ // throws IllegalStateException. Catch the exception to avoid crashing the process.
+ Slogf.w(TAG, e, "Cannot unregister from car watchdog daemon");
}
}
@@ -627,7 +651,9 @@
userId, userStateDesc);
}
} catch (RemoteException | RuntimeException e) {
- Slogf.w(TAG, "Notifying user state change failed: %s", e);
+ // When car watchdog daemon is not connected, the {@link mCarWatchdogDaemonHelper}
+ // throws IllegalStateException. Catch the exception to avoid crashing the process.
+ Slogf.w(TAG, e, "Notifying user state change failed");
}
});
}
diff --git a/service/src/com/android/car/watchdog/WatchdogPerfHandler.java b/service/src/com/android/car/watchdog/WatchdogPerfHandler.java
index 9d479c6..d39ec88 100644
--- a/service/src/com/android/car/watchdog/WatchdogPerfHandler.java
+++ b/service/src/com/android/car/watchdog/WatchdogPerfHandler.java
@@ -155,7 +155,8 @@
public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA = "MEDIA";
public static final String INTERNAL_APPLICATION_CATEGORY_TYPE_UNKNOWN = "UNKNOWN";
public static final int UID_IO_USAGE_SUMMARY_TOP_COUNT = 10;
- public static final int UID_IO_USAGE_SUMMARY_MIN_WEEKLY_WRITTEN_BYTES = 500 * 1024 * 1024;
+ public static final int UID_IO_USAGE_SUMMARY_MIN_SYSTEM_TOTAL_WEEKLY_WRITTEN_BYTES =
+ 500 * 1024 * 1024;
static final String INTENT_EXTRA_ID = "notification_id";
@@ -1847,7 +1848,7 @@
// for some user packages, the fetched summaries will still contain enough entries to pull.
List<WatchdogStorage.UserPackageDailySummaries> topUsersDailyIoUsageSummaries =
mWatchdogStorage.getTopUsersDailyIoUsageSummaries(numTopUsers * 2,
- UID_IO_USAGE_SUMMARY_MIN_WEEKLY_WRITTEN_BYTES,
+ UID_IO_USAGE_SUMMARY_MIN_SYSTEM_TOTAL_WEEKLY_WRITTEN_BYTES,
period.first.toEpochSecond(), period.second.toEpochSecond());
if (topUsersDailyIoUsageSummaries == null) {
Slogf.i(TAG, "No top users' I/O usage summary stats available to pull");
diff --git a/service/src/com/android/car/watchdog/WatchdogStorage.java b/service/src/com/android/car/watchdog/WatchdogStorage.java
index f4ae576..57baf06 100644
--- a/service/src/com/android/car/watchdog/WatchdogStorage.java
+++ b/service/src/com/android/car/watchdog/WatchdogStorage.java
@@ -228,13 +228,17 @@
* when summaries are not available.
*/
public @Nullable List<UserPackageDailySummaries> getTopUsersDailyIoUsageSummaries(
- int numTopUsers, long minTotalWrittenBytes, long includingStartEpochSeconds,
+ int numTopUsers, long minSystemTotalWrittenBytes, long includingStartEpochSeconds,
long excludingEndEpochSeconds) {
ArrayMap<String, List<AtomsProto.CarWatchdogDailyIoUsageSummary>> summariesById;
try (SQLiteDatabase db = mDbHelper.getReadableDatabase()) {
+ long systemTotalWrittenBytes = IoUsageStatsTable.querySystemTotalWrittenBytes(db,
+ includingStartEpochSeconds, excludingEndEpochSeconds);
+ if (systemTotalWrittenBytes < minSystemTotalWrittenBytes) {
+ return null;
+ }
summariesById = IoUsageStatsTable.queryTopUsersDailyIoUsageSummaries(db,
- numTopUsers, minTotalWrittenBytes, includingStartEpochSeconds,
- excludingEndEpochSeconds);
+ numTopUsers, includingStartEpochSeconds, excludingEndEpochSeconds);
}
if (summariesById == null) {
return null;
@@ -933,8 +937,6 @@
.append(COLUMN_WRITTEN_GARAGE_MODE_BYTES).append(") > 0 ")
.append("ORDER BY stats_date_epoch ASC");
- Slogf.e(TAG, "Query: %s", queryBuilder.toString());
-
String[] selectionArgs = new String[]{String.valueOf(includingStartEpochSeconds),
String.valueOf(excludingEndEpochSeconds)};
List<AtomsProto.CarWatchdogDailyIoUsageSummary> summaries = new ArrayList<>();
@@ -955,10 +957,30 @@
return summaries;
}
+ public static long querySystemTotalWrittenBytes(SQLiteDatabase db,
+ long includingStartEpochSeconds, long excludingEndEpochSeconds) {
+ StringBuilder queryBuilder = new StringBuilder();
+ queryBuilder.append("SELECT SUM(").append(COLUMN_WRITTEN_FOREGROUND_BYTES).append(" + ")
+ .append(COLUMN_WRITTEN_BACKGROUND_BYTES).append(" + ")
+ .append(COLUMN_WRITTEN_GARAGE_MODE_BYTES).append(") ")
+ .append("FROM ").append(TABLE_NAME).append(" WHERE ")
+ .append(COLUMN_DATE_EPOCH).append(" >= ? and ")
+ .append(COLUMN_DATE_EPOCH).append(" < ? ");
+
+ String[] selectionArgs = new String[]{String.valueOf(includingStartEpochSeconds),
+ String.valueOf(excludingEndEpochSeconds)};
+ long totalWrittenBytes = 0;
+ try (Cursor cursor = db.rawQuery(queryBuilder.toString(), selectionArgs)) {
+ while (cursor.moveToNext()) {
+ totalWrittenBytes += cursor.getLong(0);
+ }
+ }
+ return totalWrittenBytes;
+ }
+
public static @Nullable ArrayMap<String, List<AtomsProto.CarWatchdogDailyIoUsageSummary>>
queryTopUsersDailyIoUsageSummaries(SQLiteDatabase db, int numTopUsers,
- long minTotalWrittenBytes, long includingStartEpochSeconds,
- long excludingEndEpochSeconds) {
+ long includingStartEpochSeconds, long excludingEndEpochSeconds) {
StringBuilder innerQueryBuilder = new StringBuilder();
innerQueryBuilder.append("SELECT ").append(COLUMN_USER_PACKAGE_ID)
.append(" FROM (SELECT ").append(COLUMN_USER_PACKAGE_ID).append(", ")
@@ -969,8 +991,8 @@
.append(COLUMN_DATE_EPOCH).append(" >= ? and ")
.append(COLUMN_DATE_EPOCH).append(" < ?")
.append(" GROUP BY ").append(COLUMN_USER_PACKAGE_ID)
- .append(" HAVING total_written_bytes >= ").append(minTotalWrittenBytes)
- .append(" ORDER BY total_written_bytes LIMIT ").append(numTopUsers).append(')');
+ .append(" ORDER BY total_written_bytes DESC LIMIT ").append(numTopUsers)
+ .append(')');
StringBuilder queryBuilder = new StringBuilder();
queryBuilder.append("SELECT ").append(COLUMN_USER_PACKAGE_ID).append(", ")
diff --git a/tests/carservice_unit_test/src/com/android/car/watchdog/CarWatchdogServiceUnitTest.java b/tests/carservice_unit_test/src/com/android/car/watchdog/CarWatchdogServiceUnitTest.java
index c446c05..5a745e9 100644
--- a/tests/carservice_unit_test/src/com/android/car/watchdog/CarWatchdogServiceUnitTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/watchdog/CarWatchdogServiceUnitTest.java
@@ -39,9 +39,10 @@
import static com.android.car.watchdog.CarWatchdogService.ACTION_DISMISS_RESOURCE_OVERUSE_NOTIFICATION;
import static com.android.car.watchdog.CarWatchdogService.ACTION_LAUNCH_APP_SETTINGS;
import static com.android.car.watchdog.CarWatchdogService.ACTION_RESOURCE_OVERUSE_DISABLE_APP;
+import static com.android.car.watchdog.CarWatchdogService.MISSING_ARG_VALUE;
import static com.android.car.watchdog.TimeSource.ZONE_OFFSET;
import static com.android.car.watchdog.WatchdogPerfHandler.INTENT_EXTRA_ID;
-import static com.android.car.watchdog.WatchdogPerfHandler.UID_IO_USAGE_SUMMARY_MIN_WEEKLY_WRITTEN_BYTES;
+import static com.android.car.watchdog.WatchdogPerfHandler.UID_IO_USAGE_SUMMARY_MIN_SYSTEM_TOTAL_WEEKLY_WRITTEN_BYTES;
import static com.android.car.watchdog.WatchdogStorage.RETENTION_PERIOD;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -373,9 +374,8 @@
public void testGarageModeStateChangeToOn() throws Exception {
mBroadcastReceiver.onReceive(mMockContext,
new Intent().setAction(CarWatchdogService.ACTION_GARAGE_MODE_ON));
- verify(mMockCarWatchdogDaemon)
- .notifySystemStateChange(
- eq(StateType.GARAGE_MODE), eq(GarageMode.GARAGE_MODE_ON), eq(-1));
+ verify(mMockCarWatchdogDaemon).notifySystemStateChange(StateType.GARAGE_MODE,
+ GarageMode.GARAGE_MODE_ON, MISSING_ARG_VALUE);
verify(mMockWatchdogStorage).shrinkDatabase();
}
@@ -385,9 +385,8 @@
new Intent().setAction(CarWatchdogService.ACTION_GARAGE_MODE_OFF));
// GARAGE_MODE_OFF is notified twice: Once during the initial daemon connect and once when
// the ACTION_GARAGE_MODE_OFF intent is received.
- verify(mMockCarWatchdogDaemon, times(2))
- .notifySystemStateChange(
- eq(StateType.GARAGE_MODE), eq(GarageMode.GARAGE_MODE_OFF), eq(-1));
+ verify(mMockCarWatchdogDaemon, times(2)).notifySystemStateChange(StateType.GARAGE_MODE,
+ GarageMode.GARAGE_MODE_OFF, MISSING_ARG_VALUE);
verify(mMockWatchdogStorage, never()).shrinkDatabase();
}
@@ -404,14 +403,14 @@
restartWatchdogDaemonAndAwait();
- verify(mMockCarWatchdogDaemon, times(1)).notifySystemStateChange(
- eq(StateType.USER_STATE), eq(101), eq(UserState.USER_STATE_STOPPED));
- verify(mMockCarWatchdogDaemon, times(1)).notifySystemStateChange(
- eq(StateType.USER_STATE), eq(102), eq(UserState.USER_STATE_STARTED));
- verify(mMockCarWatchdogDaemon, times(1)).notifySystemStateChange(
- eq(StateType.POWER_CYCLE), eq(PowerCycle.POWER_CYCLE_SHUTDOWN_ENTER), eq(-1));
- verify(mMockCarWatchdogDaemon, times(1)).notifySystemStateChange(
- eq(StateType.GARAGE_MODE), eq(GarageMode.GARAGE_MODE_ON), eq(-1));
+ verify(mMockCarWatchdogDaemon).notifySystemStateChange(StateType.USER_STATE, 101,
+ UserState.USER_STATE_STOPPED);
+ verify(mMockCarWatchdogDaemon).notifySystemStateChange(StateType.USER_STATE, 102,
+ UserState.USER_STATE_STARTED);
+ verify(mMockCarWatchdogDaemon).notifySystemStateChange(StateType.POWER_CYCLE,
+ PowerCycle.POWER_CYCLE_SHUTDOWN_ENTER, MISSING_ARG_VALUE);
+ verify(mMockCarWatchdogDaemon).notifySystemStateChange(StateType.GARAGE_MODE,
+ GarageMode.GARAGE_MODE_ON, MISSING_ARG_VALUE);
}
@Test
@@ -420,6 +419,8 @@
mBroadcastReceiver.onReceive(mMockContext,
new Intent().setAction(Intent.ACTION_USER_REMOVED)
.putExtra(Intent.EXTRA_USER, UserHandle.of(100)));
+ verify(mMockCarWatchdogDaemon).notifySystemStateChange(StateType.USER_STATE, 100,
+ UserState.USER_STATE_REMOVED);
verify(mMockWatchdogStorage).syncUsers(new int[] {101, 102});
}
@@ -3689,7 +3690,7 @@
.that(mWatchdogServiceForSystemImpl).isNotNull();
verify(mMockCarWatchdogDaemon, atLeastOnce()).notifySystemStateChange(
- eq(StateType.GARAGE_MODE), eq(GarageMode.GARAGE_MODE_OFF), eq(-1));
+ StateType.GARAGE_MODE, GarageMode.GARAGE_MODE_OFF, MISSING_ARG_VALUE);
// Once registration with daemon completes, the service post a new message on the main
// thread to fetch and sync resource overuse configs.
@@ -4470,7 +4471,7 @@
long startEpochSecond = beginWeekStartDate.toEpochSecond();
verify(mMockWatchdogStorage).getTopUsersDailyIoUsageSummaries(
UID_IO_USAGE_SUMMARY_TOP_COUNT * 2,
- UID_IO_USAGE_SUMMARY_MIN_WEEKLY_WRITTEN_BYTES, startEpochSecond,
+ UID_IO_USAGE_SUMMARY_MIN_SYSTEM_TOTAL_WEEKLY_WRITTEN_BYTES, startEpochSecond,
beginWeekStartDate.plusWeeks(1).toEpochSecond());
for (Integer uid : expectUids) {
expectedSummaries.add(AtomsProto.CarWatchdogUidIoUsageSummary.newBuilder()
diff --git a/tests/carservice_unit_test/src/com/android/car/watchdog/WatchdogStorageUnitTest.java b/tests/carservice_unit_test/src/com/android/car/watchdog/WatchdogStorageUnitTest.java
index 614809f..84b8672 100644
--- a/tests/carservice_unit_test/src/com/android/car/watchdog/WatchdogStorageUnitTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/watchdog/WatchdogStorageUnitTest.java
@@ -346,7 +346,7 @@
List<WatchdogStorage.UserPackageDailySummaries> actual =
mService.getTopUsersDailyIoUsageSummaries(/* numTopUsers= */ 3,
- /* minTotalWrittenBytes= */ 600_000,
+ /* minSystemTotalWrittenBytes= */ 600_000,
/* includingStartEpochSeconds= */ currentDate.minusDays(15).toEpochSecond(),
/* excludingEndEpochSeconds= */ currentDate.minusDays(7).toEpochSecond());
@@ -354,6 +354,8 @@
new ArrayList<>();
List<AtomsProto.CarWatchdogDailyIoUsageSummary> user100VendorPkgSummaries =
new ArrayList<>();
+ List<AtomsProto.CarWatchdogDailyIoUsageSummary> user101SystemPkgSummaries =
+ new ArrayList<>();
for (int i = 15; i > 7; --i) {
user101VendorPkgSummaries.add(CarWatchdogServiceUnitTest
.constructCarWatchdogDailyIoUsageSummary(/* fgWrBytes= */ 4101L * i,
@@ -363,17 +365,47 @@
.constructCarWatchdogDailyIoUsageSummary(/* fgWrBytes= */ 4100L * i,
/* bgWrBytes= */ 5100L * i, /* gmWrBytes= */ 6100L * i,
/* overuseCount= */ 1));
+ user101SystemPkgSummaries.add(CarWatchdogServiceUnitTest
+ .constructCarWatchdogDailyIoUsageSummary(/* fgWrBytes= */ 1101L * i,
+ /* bgWrBytes= */ 2101L * i, /* gmWrBytes= */ 3101L * i,
+ /* overuseCount= */ 2));
}
List<WatchdogStorage.UserPackageDailySummaries> expected = Arrays.asList(
new WatchdogStorage.UserPackageDailySummaries(/* userId= */ 101,
/* packageName= */ "vendor_package.critical.C", user101VendorPkgSummaries),
new WatchdogStorage.UserPackageDailySummaries(/* userId= */ 100,
- /* packageName= */ "vendor_package.critical.C", user100VendorPkgSummaries));
+ /* packageName= */ "vendor_package.critical.C", user100VendorPkgSummaries),
+ new WatchdogStorage.UserPackageDailySummaries(/* userId= */ 101,
+ /* packageName= */ "system_package.non_critical.A",
+ user101SystemPkgSummaries));
assertWithMessage("Top users daily I/O usage summaries").that(actual).isEqualTo(expected);
}
@Test
+ public void testGetTopUsersDailyIoUsageSummariesWithLowSystemTotalWrittenBytes()
+ throws Exception {
+ injectSampleUserPackageSettings();
+ List<WatchdogStorage.IoUsageStatsEntry> entries = new ArrayList<>();
+ for (int i = 1; i <= 30; ++i) {
+ entries.addAll(sampleStatsBetweenDates(/* includingStartDaysAgo= */ i,
+ /* excludingEndDaysAgo= */ i + 1, /* writtenBytesMultiplier= */ i));
+ }
+
+ assertWithMessage("Save I/O usage stats").that(mService.saveIoUsageStats(entries)).isTrue();
+
+ ZonedDateTime currentDate = mTimeSource.getCurrentDate();
+
+ List<WatchdogStorage.UserPackageDailySummaries> actual =
+ mService.getTopUsersDailyIoUsageSummaries(/* numTopUsers= */ 3,
+ /* minSystemTotalWrittenBytes= */ 4_000_000,
+ /* includingStartEpochSeconds= */ currentDate.minusDays(15).toEpochSecond(),
+ /* excludingEndEpochSeconds= */ currentDate.minusDays(7).toEpochSecond());
+
+ assertWithMessage("Top users daily I/O usage summaries").that(actual).isNull();
+ }
+
+ @Test
public void testGetTopUsersDailyIoUsageSummariesWithoutStats() throws Exception {
injectSampleUserPackageSettings();
List<WatchdogStorage.IoUsageStatsEntry> entries = new ArrayList<>();
@@ -388,7 +420,7 @@
List<WatchdogStorage.UserPackageDailySummaries> actual =
mService.getTopUsersDailyIoUsageSummaries(/* numTopUsers= */ 3,
- /* minTotalWrittenBytes= */ 600_000,
+ /* minSystemTotalWrittenBytes= */ 600_000,
/* includingStartEpochSeconds= */ currentDate.minusDays(15).toEpochSecond(),
/* excludingEndEpochSeconds= */ currentDate.minusDays(7).toEpochSecond());