Camera: Fix handling of dead processes for arbitration
A dead (or dying) process will have state NONEXISTENT, OOM score
INVALID when queried from ProcessInfoService. Ensure that this maps
to a very low total score for prioritization.
Also add unit testing for this, and improve the unit tests and service
code to use named constants instead of magic numbers for state/score.
Test: New unit tests pass; CTS not regressed on coral
Bug: 181777896
Change-Id: I19f1a7e0b75f35e64ff790ef7098b3d40b7e55be
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 706197e..99d10d2 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -128,10 +128,9 @@
static const String16 sCameraOpenCloseListenerPermission(
"android.permission.CAMERA_OPEN_CLOSE_LISTENER");
-// Matches with PERCEPTIBLE_APP_ADJ in ProcessList.java
-static constexpr int32_t kVendorClientScore = 200;
-// Matches with PROCESS_STATE_PERSISTENT_UI in ActivityManager.java
-static constexpr int32_t kVendorClientState = 1;
+static constexpr int32_t kVendorClientScore = resource_policy::PERCEPTIBLE_APP_ADJ;
+static constexpr int32_t kVendorClientState = ActivityManager::PROCESS_STATE_PERSISTENT_UI;
+
const String8 CameraService::kOfflineDevice("offline-");
CameraService::CameraService() :
diff --git a/services/camera/libcameraservice/tests/ClientManagerTest.cpp b/services/camera/libcameraservice/tests/ClientManagerTest.cpp
index 6a38427..037c5c2 100644
--- a/services/camera/libcameraservice/tests/ClientManagerTest.cpp
+++ b/services/camera/libcameraservice/tests/ClientManagerTest.cpp
@@ -17,10 +17,13 @@
#define LOG_NDEBUG 0
#define LOG_TAG "ClientManagerTest"
+#include <binder/ActivityManager.h>
+
#include "../utils/ClientManager.h"
#include <gtest/gtest.h>
using namespace android::resource_policy;
+using namespace android;
struct TestClient {
TestClient(int id, int32_t cost, const std::set<int>& conflictingKeys, int32_t ownerId,
@@ -59,13 +62,15 @@
TestClientManager cm;
TestClient cam0Client(/*ID*/0, /*cost*/100, /*conflicts*/{1},
- /*ownerId*/ 1000, /*score*/50, /*state*/ 1, /*isVendorClient*/ false);
+ /*ownerId*/ 1000, PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ,
+ ActivityManager::PROCESS_STATE_PERSISTENT_UI, /*isVendorClient*/ false);
auto cam0Desc = makeDescFromTestClient(cam0Client);
auto evicted = cm.addAndEvict(cam0Desc);
ASSERT_EQ(evicted.size(), 0u) << "Evicted list must be empty";
TestClient cam1Client(/*ID*/1, /*cost*/100, /*conflicts*/{0},
- /*ownerId*/ 1000, /*score*/50, /*state*/ 1, /*isVendorClient*/ false);
+ /*ownerId*/ 1000, PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ,
+ ActivityManager::PROCESS_STATE_PERSISTENT_UI, /*isVendorClient*/ false);
auto cam1Desc = makeDescFromTestClient(cam1Client);
// 1. Check with conflicting devices, new client would be evicted
@@ -76,13 +81,15 @@
cm.removeAll();
TestClient cam2Client(/*ID*/2, /*cost*/100, /*conflicts*/{},
- /*ownerId*/ 1000, /*score*/50, /*state*/ 1, /*isVendorClient*/ false);
+ /*ownerId*/ 1000, PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ,
+ ActivityManager::PROCESS_STATE_PERSISTENT_UI, /*isVendorClient*/ false);
auto cam2Desc = makeDescFromTestClient(cam2Client);
evicted = cm.addAndEvict(cam2Desc);
ASSERT_EQ(evicted.size(), 0u) << "Evicted list must be empty";
TestClient cam3Client(/*ID*/3, /*cost*/100, /*conflicts*/{},
- /*ownerId*/ 1000, /*score*/50, /*state*/ 1, /*isVendorClient*/ false);
+ /*ownerId*/ 1000, PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ,
+ ActivityManager::PROCESS_STATE_PERSISTENT_UI, /*isVendorClient*/ false);
auto cam3Desc = makeDescFromTestClient(cam3Client);
// 2. Check without conflicting devices, the pre-existing client won't be evicted
@@ -97,12 +104,42 @@
ASSERT_EQ(evicted.size(), 0u) << "Evicted list must be empty";
TestClient cam0ClientNew(/*ID*/0, /*cost*/100, /*conflicts*/{1},
- /*ownerId*/ 1000, /*score*/50, /*state*/ 1, /*isVendorClient*/ false);
+ /*ownerId*/ 1000, PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ,
+ ActivityManager::PROCESS_STATE_PERSISTENT_UI, /*isVendorClient*/ false);
auto cam0DescNew = makeDescFromTestClient(cam0ClientNew);
wouldBeEvicted = cm.wouldEvict(cam0DescNew);
// 3. Check opening the same camera twice will evict the older client
ASSERT_EQ(wouldBeEvicted.size(), 1u) << "Evicted list length must be 1";
ASSERT_EQ(wouldBeEvicted[0], cam0Desc) << "cam0 (old) must be evicted";
-}
+ // 4. Check that an invalid client (dead process) will be evicted
+
+ cm.removeAll();
+
+ TestClient camDeadClient(/*ID*/ 0, /*cost*/100, /*conflicts*/{},
+ /*ownerId*/ 1000, INVALID_ADJ,
+ ActivityManager::PROCESS_STATE_NONEXISTENT, /*isVendorClient*/ false);
+ auto camDeadDesc = makeDescFromTestClient(camDeadClient);
+ evicted = cm.addAndEvict(camDeadDesc);
+ wouldBeEvicted = cm.wouldEvict(cam0Desc);
+
+ ASSERT_EQ(evicted.size(), 0u) << "Evicted list must be empty";
+ ASSERT_EQ(wouldBeEvicted.size(), 1u) << "Evicted list length must be 1";
+ ASSERT_EQ(wouldBeEvicted[0], camDeadDesc) << "dead cam must be evicted";
+
+ // 5. Check that a more important client will win
+
+ TestClient cam0ForegroundClient(/*ID*/0, /*cost*/100, /*conflicts*/{1},
+ /*ownerId*/ 1000, FOREGROUND_APP_ADJ,
+ ActivityManager::PROCESS_STATE_PERSISTENT_UI, /*isVendorClient*/ false);
+ auto cam0FgDesc = makeDescFromTestClient(cam0ForegroundClient);
+
+ cm.removeAll();
+ evicted = cm.addAndEvict(cam0Desc);
+ wouldBeEvicted = cm.wouldEvict(cam0FgDesc);
+
+ ASSERT_EQ(evicted.size(), 0u);
+ ASSERT_EQ(wouldBeEvicted.size(), 1u);
+ ASSERT_EQ(wouldBeEvicted[0],cam0Desc) << "less important cam0 must be evicted";
+}
diff --git a/services/camera/libcameraservice/utils/ClientManager.h b/services/camera/libcameraservice/utils/ClientManager.h
index 64be6c5..09258ef 100644
--- a/services/camera/libcameraservice/utils/ClientManager.h
+++ b/services/camera/libcameraservice/utils/ClientManager.h
@@ -31,6 +31,31 @@
namespace android {
namespace resource_policy {
+// Values from frameworks/base/services/core/java/com/android/server/am/ProcessList.java
+const int32_t INVALID_ADJ = -10000;
+const int32_t UNKNOWN_ADJ = 1001;
+const int32_t CACHED_APP_MAX_ADJ = 999;
+const int32_t CACHED_APP_MIN_ADJ = 900;
+const int32_t CACHED_APP_LMK_FIRST_ADJ = 950;
+const int32_t CACHED_APP_IMPORTANCE_LEVELS = 5;
+const int32_t SERVICE_B_ADJ = 800;
+const int32_t PREVIOUS_APP_ADJ = 700;
+const int32_t HOME_APP_ADJ = 600;
+const int32_t SERVICE_ADJ = 500;
+const int32_t HEAVY_WEIGHT_APP_ADJ = 400;
+const int32_t BACKUP_APP_ADJ = 300;
+const int32_t PERCEPTIBLE_LOW_APP_ADJ = 250;
+const int32_t PERCEPTIBLE_MEDIUM_APP_ADJ = 225;
+const int32_t PERCEPTIBLE_APP_ADJ = 200;
+const int32_t VISIBLE_APP_ADJ = 100;
+const int32_t VISIBLE_APP_LAYER_MAX = PERCEPTIBLE_APP_ADJ - VISIBLE_APP_ADJ - 1;
+const int32_t PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ = 50;
+const int32_t FOREGROUND_APP_ADJ = 0;
+const int32_t PERSISTENT_SERVICE_ADJ = -700;
+const int32_t PERSISTENT_PROC_ADJ = -800;
+const int32_t SYSTEM_ADJ = -900;
+const int32_t NATIVE_ADJ = -1000;
+
class ClientPriority {
public:
/**
@@ -40,7 +65,9 @@
* hwbinder thread.
*/
ClientPriority(int32_t score, int32_t state, bool isVendorClient) :
- mScore(score), mState(state), mIsVendorClient(isVendorClient) { }
+ mScore((score == INVALID_ADJ) ? UNKNOWN_ADJ : score),
+ mState(state),
+ mIsVendorClient(isVendorClient) { }
int32_t getScore() const { return mScore; }
int32_t getState() const { return mState; }
@@ -50,7 +77,7 @@
// construction. Otherwise, it can get reset each time cameraserver
// queries ActivityManagerService for oom_adj scores / states .
if (!mIsVendorClient) {
- mScore = score;
+ mScore = (score == INVALID_ADJ) ? UNKNOWN_ADJ : score;
}
}