Add new onUidProcAdjChanged callback to be consumed by the camera
service.

The camera service needs to know when individual process oom adj scores
are changed in order to address bug #124224342. When two separate
processes are displayed in split screen and focus is switched between
them, both proc states will remain the same while their oom scores
change. This is a problem if both have access to the camera - we want
only one owner of the camera stream at any given time and for the app
in focus to be the one to own it.

This patch adds a new IUidObserver registration level for individual
process oom score changes. In addition a new callback has been added to
IUidObserver to track these changes.

Change-Id: I68d964f474d20f819f54b614a4e314ce00aac8fb
Bug: 124224342
Test: -- ActivityManagerServiceTest
      -- ActivityManagerProcessStateTest
      -- ActivityManagerFgsBgStartTest
      -- UidObserverControllerTest
      -- NetworkPolicyManagerServiceTest
      -- ShortcutManagerTest2
      -- HintManagerServiceTest
      -- VibrationSettingsTest
      -- CameraEvictionTest#testCamera2AccessCallbackInSplitMode (x100)
diff --git a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
index 70d5038..fab5b5f 100644
--- a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
+++ b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
@@ -767,6 +767,10 @@
         @Override
         public void onUidCachedChanged(int uid, boolean cached) {
         }
+
+        @Override
+        public void onUidProcAdjChanged(int uid) {
+        }
     }
 
     private final class AppOpsWatcher extends IAppOpsCallback.Stub {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 2028be7..c4caa6c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1009,6 +1009,9 @@
 
         @Override public void onUidCachedChanged(int uid, boolean cached) {
         }
+
+        @Override public void onUidProcAdjChanged(int uid) {
+        }
     };
 
     public Context getTestableContext() {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index ee0f9e8..b3e81cb 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -404,6 +404,10 @@
         @Override
         public void onUidCachedChanged(int uid, boolean cached) {
         }
+
+        @Override
+        public void onUidProcAdjChanged(int uid) {
+        }
     }
 
     /**
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java
index 5ec2f56..3578c8a 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java
@@ -97,6 +97,10 @@
         @Override
         public void onUidCachedChanged(int uid, boolean cached) {
         }
+
+        @Override
+        public void onUidProcAdjChanged(int uid) {
+        }
     };
 
     ProcessStateModifier(@NonNull InternalResourceService irs) {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 5b05b45..7aefbae 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -222,6 +222,9 @@
 
         @Override public void onUidCachedChanged(int uid, boolean cached) {
         }
+
+        @Override public void onUidProcAdjChanged(int uid) {
+        }
     }
 
     final ArrayMap<OnUidImportanceListener, UidObserver> mImportanceListeners = new ArrayMap<>();
@@ -825,6 +828,9 @@
     /** @hide Flag for registerUidObserver: report uid capability has changed. */
     public static final int UID_OBSERVER_CAPABILITY = 1<<5;
 
+    /** @hide Flag for registerUidObserver: report pid oom adj has changed. */
+    public static final int UID_OBSERVER_PROC_OOM_ADJ = 1 << 6;
+
     /** @hide Mode for {@link IActivityManager#isAppStartModeDisabled}: normal free-to-run operation. */
     public static final int APP_START_MODE_NORMAL = 0;
 
diff --git a/core/java/android/app/IUidObserver.aidl b/core/java/android/app/IUidObserver.aidl
index 74018a8..0c920f1 100644
--- a/core/java/android/app/IUidObserver.aidl
+++ b/core/java/android/app/IUidObserver.aidl
@@ -54,6 +54,13 @@
      */
     void onUidStateChanged(int uid, int procState, long procStateSeq, int capability);
 
+    /**
+     * Report a proc oom adj change associated with a uid.
+     *
+     * @param uid The uid for which the state change is being reported.
+     */
+    void onUidProcAdjChanged(int uid);
+
     // =============== End of transactions used on native side as well ============================
 
     /**
diff --git a/core/proto/android/app/activitymanager.proto b/core/proto/android/app/activitymanager.proto
index a21d1d4..8d8759f 100644
--- a/core/proto/android/app/activitymanager.proto
+++ b/core/proto/android/app/activitymanager.proto
@@ -35,4 +35,6 @@
     UID_OBSERVER_FLAG_CACHED = 5;
     // report uid capability has changed, original value is 1 << 5
     UID_OBSERVER_FLAG_CAPABILITY = 6;
+    // report pid oom adj has changed, original value is 1 << 6
+    UID_OBSERVER_FLAG_PROC_OOM_ADJ = 7;
 }
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 2bb8912..7205dd8 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -780,6 +780,7 @@
         CHANGE_UNCACHED = 4;
         CHANGE_CAPABILITY = 5;
         CHANGE_PROCSTATE = 6;
+        CHANGE_PROCADJ = 7;
     }
     repeated Change last_reported_changes = 8;
     optional int32 num_procs = 9;
diff --git a/native/android/activity_manager.cpp b/native/android/activity_manager.cpp
index 82f4569..155a355 100644
--- a/native/android/activity_manager.cpp
+++ b/native/android/activity_manager.cpp
@@ -45,6 +45,7 @@
     void onUidIdle(uid_t uid, bool disabled) override;
     void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq,
                            int32_t capability) override;
+    void onUidProcAdjChanged(uid_t uid) override;
 
     // IBinder::DeathRecipient implementation
     void binderDied(const wp<IBinder>& who) override;
@@ -120,6 +121,8 @@
 
 void UidObserver::onUidIdle(uid_t uid __unused, bool disabled __unused) {}
 
+void UidObserver::onUidProcAdjChanged(uid_t uid __unused) {}
+
 void UidObserver::onUidStateChanged(uid_t uid, int32_t procState,
                                     int64_t procStateSeq __unused,
                                     int32_t capability __unused) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 6ee65ae..ec44f18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -394,6 +394,7 @@
         override fun onUidActive(uid: Int) {}
         override fun onUidIdle(uid: Int, disabled: Boolean) {}
         override fun onUidCachedChanged(uid: Int, cached: Boolean) {}
+        override fun onUidProcAdjChanged(uid: Int) {}
     }
 }
 
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index 16ff167..c5eb25b 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -385,6 +385,10 @@
                 @Override
                 public void onUidCachedChanged(int uid, boolean cached) throws RemoteException {
                 }
+
+                @Override
+                public void onUidProcAdjChanged(int uid) throws RemoteException {
+                }
             }, UID_OBSERVER_GONE | UID_OBSERVER_ACTIVE, 0, null);
         } catch (RemoteException e) {
             Slog.e(TAG, "Failed to register uid observer", e);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 45f06c74..1521861 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -15257,7 +15257,7 @@
             }
             if ((enqueuedChange & UidRecord.CHANGE_GONE) != 0) {
                 mLocalPowerManager.uidGone(uid);
-            } else {
+            } else if ((enqueuedChange & UidRecord.CHANGE_PROCSTATE) != 0) {
                 mLocalPowerManager.updateUidProcState(uid, procState);
             }
         }
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 2b61e7f..1d39d4a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -1671,6 +1671,10 @@
         }
 
         @Override
+        public void onUidProcAdjChanged(int uid) throws RemoteException {
+        }
+
+        @Override
         public void onOomAdjMessage(String msg) {
             synchronized (this) {
                 final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java
index 41ff083..d758a7f 100644
--- a/services/core/java/com/android/server/am/AppRestrictionController.java
+++ b/services/core/java/com/android/server/am/AppRestrictionController.java
@@ -908,6 +908,10 @@
                 @Override
                 public void onUidCachedChanged(int uid, boolean cached) {
                 }
+
+                @Override
+                public void onUidProcAdjChanged(int uid) {
+                }
             };
 
     /**
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 85d3d5d..85f2a92 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1191,70 +1191,88 @@
         }
         for (int i = activeUids.size() - 1; i >= 0; i--) {
             final UidRecord uidRec = activeUids.valueAt(i);
-            int uidChange = 0;
-            if (uidRec.getCurProcState() != PROCESS_STATE_NONEXISTENT
-                    && (uidRec.getSetProcState() != uidRec.getCurProcState()
-                    || uidRec.getSetCapability() != uidRec.getCurCapability()
-                    || uidRec.isSetAllowListed() != uidRec.isCurAllowListed())) {
-                if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, "Changes in " + uidRec
-                        + ": proc state from " + uidRec.getSetProcState() + " to "
-                        + uidRec.getCurProcState() + ", capability from "
-                        + uidRec.getSetCapability() + " to " + uidRec.getCurCapability()
-                        + ", allowlist from " + uidRec.isSetAllowListed()
-                        + " to " + uidRec.isCurAllowListed());
-                if (ActivityManager.isProcStateBackground(uidRec.getCurProcState())
-                        && !uidRec.isCurAllowListed()) {
-                    // UID is now in the background (and not on the temp allowlist).  Was it
-                    // previously in the foreground (or on the temp allowlist)?
-                    if (!ActivityManager.isProcStateBackground(uidRec.getSetProcState())
-                            || uidRec.isSetAllowListed()) {
-                        uidRec.setLastBackgroundTime(nowElapsed);
-                        if (!mService.mHandler.hasMessages(IDLE_UIDS_MSG)) {
-                            // Note: the background settle time is in elapsed realtime, while
-                            // the handler time base is uptime.  All this means is that we may
-                            // stop background uids later than we had intended, but that only
-                            // happens because the device was sleeping so we are okay anyway.
-                            mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
-                                    mConstants.BACKGROUND_SETTLE_TIME);
+            if (uidRec.getCurProcState() != PROCESS_STATE_NONEXISTENT) {
+                if (uidRec.getSetProcState() != uidRec.getCurProcState()
+                        || uidRec.getSetCapability() != uidRec.getCurCapability()
+                        || uidRec.isSetAllowListed() != uidRec.isCurAllowListed()
+                        || uidRec.getProcAdjChanged()) {
+                    int uidChange = 0;
+                    if (DEBUG_UID_OBSERVERS) {
+                        Slog.i(TAG_UID_OBSERVERS, "Changes in " + uidRec
+                                + ": proc state from " + uidRec.getSetProcState() + " to "
+                                + uidRec.getCurProcState() + ", capability from "
+                                + uidRec.getSetCapability() + " to " + uidRec.getCurCapability()
+                                + ", allowlist from " + uidRec.isSetAllowListed()
+                                + " to " + uidRec.isCurAllowListed()
+                                + ", procAdjChanged: " + uidRec.getProcAdjChanged());
+                    }
+                    if (ActivityManager.isProcStateBackground(uidRec.getCurProcState())
+                            && !uidRec.isCurAllowListed()) {
+                        // UID is now in the background (and not on the temp allowlist).  Was it
+                        // previously in the foreground (or on the temp allowlist)?
+                        if (!ActivityManager.isProcStateBackground(uidRec.getSetProcState())
+                                || uidRec.isSetAllowListed()) {
+                            uidRec.setLastBackgroundTime(nowElapsed);
+                            if (!mService.mHandler.hasMessages(IDLE_UIDS_MSG)) {
+                                // Note: the background settle time is in elapsed realtime, while
+                                // the handler time base is uptime.  All this means is that we may
+                                // stop background uids later than we had intended, but that only
+                                // happens because the device was sleeping so we are okay anyway.
+                                mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
+                                        mConstants.BACKGROUND_SETTLE_TIME);
+                            }
                         }
+                        if (uidRec.isIdle() && !uidRec.isSetIdle()) {
+                            uidChange |= UidRecord.CHANGE_IDLE;
+                            becameIdle.add(uidRec);
+                        }
+                    } else {
+                        if (uidRec.isIdle()) {
+                            uidChange |= UidRecord.CHANGE_ACTIVE;
+                            EventLogTags.writeAmUidActive(uidRec.getUid());
+                            uidRec.setIdle(false);
+                        }
+                        uidRec.setLastBackgroundTime(0);
                     }
-                    if (uidRec.isIdle() && !uidRec.isSetIdle()) {
-                        uidChange |= UidRecord.CHANGE_IDLE;
-                        becameIdle.add(uidRec);
+                    final boolean wasCached = uidRec.getSetProcState()
+                            > ActivityManager.PROCESS_STATE_RECEIVER;
+                    final boolean isCached = uidRec.getCurProcState()
+                            > ActivityManager.PROCESS_STATE_RECEIVER;
+                    if (wasCached != isCached
+                            || uidRec.getSetProcState() == PROCESS_STATE_NONEXISTENT) {
+                        uidChange |= isCached ? UidRecord.CHANGE_CACHED :
+                                UidRecord.CHANGE_UNCACHED;
                     }
-                } else {
-                    if (uidRec.isIdle()) {
-                        uidChange |= UidRecord.CHANGE_ACTIVE;
-                        EventLogTags.writeAmUidActive(uidRec.getUid());
-                        uidRec.setIdle(false);
+                    if (uidRec.getSetCapability() != uidRec.getCurCapability()) {
+                        uidChange |= UidRecord.CHANGE_CAPABILITY;
                     }
-                    uidRec.setLastBackgroundTime(0);
-                }
-                final boolean wasCached = uidRec.getSetProcState()
-                        > ActivityManager.PROCESS_STATE_RECEIVER;
-                final boolean isCached = uidRec.getCurProcState()
-                        > ActivityManager.PROCESS_STATE_RECEIVER;
-                if (wasCached != isCached
-                        || uidRec.getSetProcState() == PROCESS_STATE_NONEXISTENT) {
-                    uidChange |= isCached ? UidRecord.CHANGE_CACHED : UidRecord.CHANGE_UNCACHED;
-                }
-                if (uidRec.getSetCapability() != uidRec.getCurCapability()) {
-                    uidChange |= UidRecord.CHANGE_CAPABILITY;
-                }
-                if (uidRec.getSetProcState() != uidRec.getCurProcState()) {
-                    uidChange |= UidRecord.CHANGE_PROCSTATE;
-                }
-                uidRec.setSetProcState(uidRec.getCurProcState());
-                uidRec.setSetCapability(uidRec.getCurCapability());
-                uidRec.setSetAllowListed(uidRec.isCurAllowListed());
-                uidRec.setSetIdle(uidRec.isIdle());
-                mService.mAtmInternal.onUidProcStateChanged(
-                        uidRec.getUid(), uidRec.getSetProcState());
-                mService.enqueueUidChangeLocked(uidRec, -1, uidChange);
-                mService.noteUidProcessState(uidRec.getUid(), uidRec.getCurProcState(),
-                        uidRec.getCurCapability());
-                if (uidRec.hasForegroundServices()) {
-                    mService.mServices.foregroundServiceProcStateChangedLocked(uidRec);
+                    if (uidRec.getSetProcState() != uidRec.getCurProcState()) {
+                        uidChange |= UidRecord.CHANGE_PROCSTATE;
+                    }
+                    if (uidRec.getProcAdjChanged()) {
+                        uidChange |= UidRecord.CHANGE_PROCADJ;
+                    }
+                    uidRec.setSetProcState(uidRec.getCurProcState());
+                    uidRec.setSetCapability(uidRec.getCurCapability());
+                    uidRec.setSetAllowListed(uidRec.isCurAllowListed());
+                    uidRec.setSetIdle(uidRec.isIdle());
+                    uidRec.clearProcAdjChanged();
+                    if ((uidChange & UidRecord.CHANGE_PROCSTATE) != 0
+                            || (uidChange & UidRecord.CHANGE_CAPABILITY) != 0) {
+                        mService.mAtmInternal.onUidProcStateChanged(
+                                uidRec.getUid(), uidRec.getSetProcState());
+                    }
+                    if (uidChange != 0) {
+                        mService.enqueueUidChangeLocked(uidRec, -1, uidChange);
+                    }
+                    if ((uidChange & UidRecord.CHANGE_PROCSTATE) != 0
+                            || (uidChange & UidRecord.CHANGE_CAPABILITY) != 0) {
+                        mService.noteUidProcessState(uidRec.getUid(), uidRec.getCurProcState(),
+                                uidRec.getCurCapability());
+                    }
+                    if (uidRec.hasForegroundServices()) {
+                        mService.mServices.foregroundServiceProcStateChangedLocked(uidRec);
+                    }
                 }
             }
             mService.mInternal.deletePendingTopUid(uidRec.getUid());
@@ -2513,6 +2531,7 @@
             long nowElapsed) {
         boolean success = true;
         final ProcessStateRecord state = app.mState;
+        final UidRecord uidRec = app.getUidRecord();
 
         if (state.getCurRawAdj() != state.getSetRawAdj()) {
             state.setSetRawAdj(state.getCurRawAdj());
@@ -2551,6 +2570,9 @@
                 reportOomAdjMessageLocked(TAG_OOM_ADJ, msg);
             }
             state.setSetAdj(state.getCurAdj());
+            if (uidRec != null) {
+                uidRec.noteProcAdjChanged();
+            }
             state.setVerifiedAdj(ProcessList.INVALID_ADJ);
         }
 
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index a8612fd..6629a30 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -180,22 +180,22 @@
     // OOM adjustments for processes in various states:
 
     // Uninitialized value for any major or minor adj fields
-    static final int INVALID_ADJ = -10000;
+    public static final int INVALID_ADJ = -10000;
 
     // Adjustment used in certain places where we don't know it yet.
     // (Generally this is something that is going to be cached, but we
     // don't know the exact value in the cached range to assign yet.)
-    static final int UNKNOWN_ADJ = 1001;
+    public static final int UNKNOWN_ADJ = 1001;
 
     // This is a process only hosting activities that are not visible,
     // so it can be killed without any disruption.
-    static final int CACHED_APP_MAX_ADJ = 999;
-    static final int CACHED_APP_MIN_ADJ = 900;
+    public static final int CACHED_APP_MAX_ADJ = 999;
+    public static final int CACHED_APP_MIN_ADJ = 900;
 
     // This is the oom_adj level that we allow to die first. This cannot be equal to
     // CACHED_APP_MAX_ADJ unless processes are actively being assigned an oom_score_adj of
     // CACHED_APP_MAX_ADJ.
-    static final int CACHED_APP_LMK_FIRST_ADJ = 950;
+    public static final int CACHED_APP_LMK_FIRST_ADJ = 950;
 
     // Number of levels we have available for different service connection group importance
     // levels.
@@ -203,7 +203,7 @@
 
     // The B list of SERVICE_ADJ -- these are the old and decrepit
     // services that aren't as shiny and interesting as the ones in the A list.
-    static final int SERVICE_B_ADJ = 800;
+    public static final int SERVICE_B_ADJ = 800;
 
     // This is the process of the previous application that the user was in.
     // This process is kept above other things, because it is very common to
@@ -211,68 +211,68 @@
     // task switch (toggling between the two top recent apps) as well as normal
     // UI flow such as clicking on a URI in the e-mail app to view in the browser,
     // and then pressing back to return to e-mail.
-    static final int PREVIOUS_APP_ADJ = 700;
+    public static final int PREVIOUS_APP_ADJ = 700;
 
     // This is a process holding the home application -- we want to try
     // avoiding killing it, even if it would normally be in the background,
     // because the user interacts with it so much.
-    static final int HOME_APP_ADJ = 600;
+    public static final int HOME_APP_ADJ = 600;
 
     // This is a process holding an application service -- killing it will not
     // have much of an impact as far as the user is concerned.
-    static final int SERVICE_ADJ = 500;
+    public static final int SERVICE_ADJ = 500;
 
     // This is a process with a heavy-weight application.  It is in the
     // background, but we want to try to avoid killing it.  Value set in
     // system/rootdir/init.rc on startup.
-    static final int HEAVY_WEIGHT_APP_ADJ = 400;
+    public static final int HEAVY_WEIGHT_APP_ADJ = 400;
 
     // This is a process currently hosting a backup operation.  Killing it
     // is not entirely fatal but is generally a bad idea.
-    static final int BACKUP_APP_ADJ = 300;
+    public static final int BACKUP_APP_ADJ = 300;
 
     // This is a process bound by the system (or other app) that's more important than services but
     // not so perceptible that it affects the user immediately if killed.
-    static final int PERCEPTIBLE_LOW_APP_ADJ = 250;
+    public static final int PERCEPTIBLE_LOW_APP_ADJ = 250;
 
     // This is a process hosting services that are not perceptible to the user but the
     // client (system) binding to it requested to treat it as if it is perceptible and avoid killing
     // it if possible.
-    static final int PERCEPTIBLE_MEDIUM_APP_ADJ = 225;
+    public static final int PERCEPTIBLE_MEDIUM_APP_ADJ = 225;
 
     // This is a process only hosting components that are perceptible to the
     // user, and we really want to avoid killing them, but they are not
     // immediately visible. An example is background music playback.
-    static final int PERCEPTIBLE_APP_ADJ = 200;
+    public static final int PERCEPTIBLE_APP_ADJ = 200;
 
     // This is a process only hosting activities that are visible to the
     // user, so we'd prefer they don't disappear.
-    static final int VISIBLE_APP_ADJ = 100;
+    public static final int VISIBLE_APP_ADJ = 100;
     static final int VISIBLE_APP_LAYER_MAX = PERCEPTIBLE_APP_ADJ - VISIBLE_APP_ADJ - 1;
 
     // This is a process that was recently TOP and moved to FGS. Continue to treat it almost
     // like a foreground app for a while.
     // @see TOP_TO_FGS_GRACE_PERIOD
-    static final int PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ = 50;
+    public static final int PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ = 50;
 
     // This is the process running the current foreground app.  We'd really
     // rather not kill it!
-    static final int FOREGROUND_APP_ADJ = 0;
+    public static final int FOREGROUND_APP_ADJ = 0;
 
     // This is a process that the system or a persistent process has bound to,
     // and indicated it is important.
-    static final int PERSISTENT_SERVICE_ADJ = -700;
+    public static final int PERSISTENT_SERVICE_ADJ = -700;
 
     // This is a system persistent process, such as telephony.  Definitely
     // don't want to kill it, but doing so is not completely fatal.
-    static final int PERSISTENT_PROC_ADJ = -800;
+    public static final int PERSISTENT_PROC_ADJ = -800;
 
     // The system process runs at the default adjustment.
-    static final int SYSTEM_ADJ = -900;
+    public static final int SYSTEM_ADJ = -900;
 
     // Special code for native processes that are not being managed by the system (so
     // don't have an oom adj assigned by the system).
-    static final int NATIVE_ADJ = -1000;
+    public static final int NATIVE_ADJ = -1000;
 
     // Memory pages are 4K.
     static final int PAGE_SIZE = 4 * 1024;
@@ -5383,5 +5383,9 @@
         @Override
         public void onUidCachedChanged(int uid, boolean cached) {
         }
+
+        @Override
+        public void onUidProcAdjChanged(int uid) {
+        }
     };
 }
diff --git a/services/core/java/com/android/server/am/UidObserverController.java b/services/core/java/com/android/server/am/UidObserverController.java
index c3a232a..e42dac4 100644
--- a/services/core/java/com/android/server/am/UidObserverController.java
+++ b/services/core/java/com/android/server/am/UidObserverController.java
@@ -157,6 +157,9 @@
         if ((pendingChange & UidRecord.CHANGE_PROCSTATE) != 0) {
             currentChange |= UidRecord.CHANGE_PROCSTATE;
         }
+        if ((pendingChange & UidRecord.CHANGE_PROCADJ) != 0) {
+            currentChange |= UidRecord.CHANGE_PROCADJ;
+        }
         return currentChange;
     }
 
@@ -248,6 +251,7 @@
         try {
             for (int j = 0; j < changesSize; j++) {
                 final ChangeRecord item = mActiveUidChanges[j];
+                final long start = SystemClock.uptimeMillis();
                 final int change = item.change;
                 if (change == UidRecord.CHANGE_PROCSTATE
                         && (reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) == 0) {
@@ -255,7 +259,12 @@
                     // interested in all proc state changes.
                     continue;
                 }
-                final long start = SystemClock.uptimeMillis();
+                if (change == UidRecord.CHANGE_PROCADJ
+                        && (reg.mWhich & ActivityManager.UID_OBSERVER_PROC_OOM_ADJ) == 0) {
+                    // No-op common case: no significant change, the observer is not
+                    // interested in proc adj changes.
+                    continue;
+                }
                 if ((change & UidRecord.CHANGE_IDLE) != 0) {
                     if ((reg.mWhich & ActivityManager.UID_OBSERVER_IDLE) != 0) {
                         if (DEBUG_UID_OBSERVERS) {
@@ -322,7 +331,12 @@
                             reg.mLastProcStates.put(item.uid, item.procState);
                         }
                         observer.onUidStateChanged(item.uid, item.procState,
-                                item.procStateSeq, item.capability);
+                                item.procStateSeq,
+                                item.capability);
+                    }
+                    if ((reg.mWhich & ActivityManager.UID_OBSERVER_PROC_OOM_ADJ) != 0
+                            && (change & UidRecord.CHANGE_PROCADJ) != 0) {
+                        observer.onUidProcAdjChanged(item.uid);
                     }
                 }
                 final int duration = (int) (SystemClock.uptimeMillis() - start);
@@ -443,6 +457,7 @@
                 ActivityManager.UID_OBSERVER_GONE,
                 ActivityManager.UID_OBSERVER_PROCSTATE,
                 ActivityManager.UID_OBSERVER_CAPABILITY,
+                ActivityManager.UID_OBSERVER_PROC_OOM_ADJ,
         };
         private static final int[] PROTO_ENUMS = new int[]{
                 ActivityManagerProto.UID_OBSERVER_FLAG_IDLE,
@@ -450,6 +465,7 @@
                 ActivityManagerProto.UID_OBSERVER_FLAG_GONE,
                 ActivityManagerProto.UID_OBSERVER_FLAG_PROCSTATE,
                 ActivityManagerProto.UID_OBSERVER_FLAG_CAPABILITY,
+                ActivityManagerProto.UID_OBSERVER_FLAG_PROC_OOM_ADJ,
         };
 
         UidObserverRegistration(int uid, @NonNull String pkg, int which, int cutpoint) {
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index 606e6fc..5c78d1e 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -48,6 +48,9 @@
     private int mSetProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
 
     @CompositeRWLock({"mService", "mProcLock"})
+    private boolean mProcAdjChanged;
+
+    @CompositeRWLock({"mService", "mProcLock"})
     private int mCurCapability;
 
     @CompositeRWLock({"mService", "mProcLock"})
@@ -126,6 +129,7 @@
     static final int CHANGE_CACHED = 1 << 3;
     static final int CHANGE_UNCACHED = 1 << 4;
     static final int CHANGE_CAPABILITY = 1 << 5;
+    static final int CHANGE_PROCADJ = 1 << 6;
     static final int CHANGE_PROCSTATE = 1 << 31;
 
     // Keep the enum lists in sync
@@ -186,6 +190,21 @@
         mSetProcState = setProcState;
     }
 
+    @GuardedBy({"mService", "mProcLock"})
+    void noteProcAdjChanged() {
+        mProcAdjChanged = true;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    void clearProcAdjChanged() {
+        mProcAdjChanged = false;
+    }
+
+    @GuardedBy({"mService", "mProcLock"})
+    boolean getProcAdjChanged() {
+        return mProcAdjChanged;
+    }
+
     @GuardedBy(anyOf = {"mService", "mProcLock"})
     int getCurCapability() {
         return mCurCapability;
@@ -428,6 +447,12 @@
                 }
                 sb.append("procstate");
             }
+            if ((mLastReportedChange & CHANGE_PROCADJ) != 0) {
+                if (printed) {
+                    sb.append("|");
+                }
+                sb.append("procadj");
+            }
         }
         sb.append(" procs:");
         sb.append(mNumProcs);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index a68fc05..dc6bd49 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -862,6 +862,9 @@
             disableAudioForUid(cached, uid);
         }
 
+        @Override public void onUidProcAdjChanged(int uid) {
+        }
+
         private void disableAudioForUid(boolean disable, int uid) {
             queueMsgUnderWakeLock(mAudioHandler, MSG_DISABLE_AUDIO_FOR_UID,
                     disable ? 1 : 0 /* arg1 */,  uid /* arg2 */,
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 589b8f1..4da8400 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1126,6 +1126,9 @@
 
         @Override public void onUidCachedChanged(int uid, boolean cached) {
         }
+
+        @Override public void onUidProcAdjChanged(int uid) {
+        }
     };
 
     private static final class UidStateCallbackInfo {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 25fe000..d024020 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -593,6 +593,9 @@
 
         @Override public void onUidCachedChanged(int uid, boolean cached) {
         }
+
+        @Override public void onUidProcAdjChanged(int uid) {
+        }
     };
 
     void handleOnUidStateChanged(int uid, int procState) {
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index 8755662..dfa1281 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -256,6 +256,10 @@
         @Override
         public void onUidCachedChanged(int uid, boolean cached) {
         }
+
+        @Override
+        public void onUidProcAdjChanged(int uid) {
+        }
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index e9535e0..bedb9f0 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -700,5 +700,9 @@
         @Override
         public void onUidCachedChanged(int uid, boolean cached) {
         }
+
+        @Override
+        public void onUidProcAdjChanged(int uid) {
+        }
     }
 }
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 366ab09..0e5c94f 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -599,6 +599,9 @@
 
         @Override public void onUidCachedChanged(int uid, boolean cached) {
         }
+
+        @Override public void onUidProcAdjChanged(int uid) {
+        }
     };
 
     @Override