1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.am;
18 
19 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
20 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW;
21 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_MODERATE;
22 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_NORMAL;
23 import static com.android.internal.app.procstats.ProcessStats.ADJ_NOTHING;
24 
25 import android.annotation.IntDef;
26 import android.os.Trace;
27 
28 import com.android.internal.annotations.GuardedBy;
29 
30 import java.lang.annotation.Retention;
31 import java.lang.annotation.RetentionPolicy;
32 
33 /**
34  * Detects low memory using PSI.
35  *
36  * If the kernel doesn't support PSI, then this class is not available.
37  */
38 public final class LowMemDetector {
39     private static final String TAG = "LowMemDetector";
40     private final ActivityManagerService mAm;
41     private final LowMemThread mLowMemThread;
42     private boolean mAvailable;
43 
44     private final Object mPressureStateLock = new Object();
45 
46     @GuardedBy("mPressureStateLock")
47     private int mPressureState = ADJ_MEM_FACTOR_NORMAL;
48 
49     public static final int ADJ_MEM_FACTOR_NOTHING = ADJ_NOTHING;
50 
51     /* getPressureState return values */
52     @IntDef(prefix = { "ADJ_MEM_FACTOR_" }, value = {
53         ADJ_MEM_FACTOR_NOTHING,
54         ADJ_MEM_FACTOR_NORMAL,
55         ADJ_MEM_FACTOR_MODERATE,
56         ADJ_MEM_FACTOR_LOW,
57         ADJ_MEM_FACTOR_CRITICAL,
58     })
59     @Retention(RetentionPolicy.SOURCE)
60     public @interface MemFactor{}
61 
LowMemDetector(ActivityManagerService am)62     LowMemDetector(ActivityManagerService am) {
63         mAm = am;
64         mLowMemThread = new LowMemThread();
65         if (init() != 0) {
66             mAvailable = false;
67         } else {
68             mAvailable = true;
69             mLowMemThread.start();
70         }
71     }
72 
isAvailable()73     public boolean isAvailable() {
74         return mAvailable;
75     }
76 
77     /**
78      * Returns the current mem factor.
79      * Note that getMemFactor returns LowMemDetector.MEM_PRESSURE_XXX
80      * which match ProcessStats.ADJ_MEM_FACTOR_XXX values. If they deviate
81      * there should be conversion performed here to translate pressure state
82      * into memFactor.
83      */
getMemFactor()84     public @MemFactor int getMemFactor() {
85         synchronized (mPressureStateLock) {
86             return mPressureState;
87         }
88     }
89 
init()90     private native int init();
waitForPressure()91     private native int waitForPressure();
92 
93     private final class LowMemThread extends Thread {
94         private boolean mIsTracingMemCriticalLow;
95 
LowMemThread()96         LowMemThread() {
97             super("LowMemThread");
98         }
99 
run()100         public void run() {
101 
102             while (true) {
103                 // sleep waiting for a PSI event
104                 int newPressureState = waitForPressure();
105                 // PSI event detected
106                 boolean isCriticalLowMemory = newPressureState == ADJ_MEM_FACTOR_CRITICAL;
107                 if (isCriticalLowMemory && !mIsTracingMemCriticalLow) {
108                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "criticalLowMemory");
109                 } else if (!isCriticalLowMemory && mIsTracingMemCriticalLow) {
110                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
111                 }
112                 mIsTracingMemCriticalLow = isCriticalLowMemory;
113                 if (newPressureState == -1) {
114                     // epoll broke, tear this down
115                     mAvailable = false;
116                     break;
117                 }
118                 // got an actual PSI event? let's update lowmem info
119                 synchronized (mPressureStateLock) {
120                     mPressureState = newPressureState;
121                 }
122             }
123         }
124     }
125 }
126