1 /*
2  * Copyright (C) 2020 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 android.os.Process.PROC_NEWLINE_TERM;
20 import static android.os.Process.PROC_OUT_LONG;
21 
22 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
23 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
24 
25 import android.os.Handler;
26 import android.os.Process;
27 import android.os.StrictMode;
28 import android.os.SystemClock;
29 import android.os.Trace;
30 import android.os.UserHandle;
31 import android.text.TextUtils;
32 import android.util.EventLog;
33 import android.util.Slog;
34 import android.util.TimeUtils;
35 
36 import com.android.internal.annotations.GuardedBy;
37 
38 import java.io.FileDescriptor;
39 import java.io.IOException;
40 import java.io.PrintWriter;
41 import java.util.function.Consumer;
42 
43 /**
44  * The "phantom" app processes, which are forked by app processes so we are not aware of
45  * them until we walk through the process list in /proc.
46  */
47 public final class PhantomProcessRecord {
48     static final String TAG = TAG_WITH_CLASS_NAME ? "PhantomProcessRecord" : TAG_AM;
49 
50     static final long[] LONG_OUT = new long[1];
51     static final int[] LONG_FORMAT = new int[] {PROC_NEWLINE_TERM | PROC_OUT_LONG};
52 
53     final String mProcessName;   // name of the process
54     final int mUid;              // uid of the process
55     final int mPid;              // The id of the process
56     final int mPpid;             // Ancestor (managed app process) pid of the process
57     final long mKnownSince;      // The timestamp when we're aware of the process
58     final FileDescriptor mPidFd; // The fd to monitor the termination of this process
59 
60     long mLastCputime;           // How long proc has run CPU at last check
61     long mCurrentCputime;        // How long proc has run CPU most recently
62     int mUpdateSeq;              // Seq no, indicating the last check on this process
63     int mAdj;                    // The last known oom adj score
64     boolean mKilled;             // Whether it has been killed by us or not
65     boolean mZombie;             // Whether it was signaled to be killed but timed out
66     String mStringName;          // Caching of the toString() result
67 
68     final ActivityManagerService mService;
69     final Object mLock;
70     final Consumer<PhantomProcessRecord> mOnKillListener;
71     final Handler mKillHandler;
72 
PhantomProcessRecord(final String processName, final int uid, final int pid, final int ppid, final ActivityManagerService service, final Consumer<PhantomProcessRecord> onKillListener)73     PhantomProcessRecord(final String processName, final int uid, final int pid,
74             final int ppid, final ActivityManagerService service,
75             final Consumer<PhantomProcessRecord> onKillListener) throws IllegalStateException {
76         mProcessName = processName;
77         mUid = uid;
78         mPid = pid;
79         mPpid = ppid;
80         mKilled = false;
81         mAdj = ProcessList.NATIVE_ADJ;
82         mKnownSince = SystemClock.elapsedRealtime();
83         mService = service;
84         mLock = service.mPhantomProcessList.mLock;
85         mOnKillListener = onKillListener;
86         mKillHandler = service.mProcessList.sKillHandler;
87         if (Process.supportsPidFd()) {
88             StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
89             try {
90                 mPidFd = Process.openPidFd(pid, 0);
91                 if (mPidFd == null) {
92                     throw new IllegalStateException();
93                 }
94             } catch (IOException e) {
95                 // Maybe a race condition, the process is gone.
96                 Slog.w(TAG, "Unable to open process " + pid + ", it might be gone");
97                 IllegalStateException ex = new IllegalStateException();
98                 ex.initCause(e);
99                 throw ex;
100             } finally {
101                 StrictMode.setThreadPolicy(oldPolicy);
102             }
103         } else {
104             mPidFd = null;
105         }
106     }
107 
getRss(int pid)108     public long getRss(int pid) {
109         long[] rss = Process.getRss(pid);
110         return (rss != null && rss.length > 0) ? rss[0] : 0;
111     }
112 
113     @GuardedBy("mLock")
killLocked(String reason, boolean noisy)114     void killLocked(String reason, boolean noisy) {
115         if (!mKilled) {
116             Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "kill");
117             if (noisy || mUid == mService.mCurOomAdjUid) {
118                 mService.reportUidInfoMessageLocked(TAG,
119                         "Killing " + toString() + ": " + reason, mUid);
120             }
121             if (mPid > 0) {
122                 EventLog.writeEvent(EventLogTags.AM_KILL, UserHandle.getUserId(mUid),
123                         mPid, mProcessName, mAdj, reason, getRss(mPid));
124                 if (!Process.supportsPidFd()) {
125                     onProcDied(false);
126                 } else {
127                     // We'll notify the listener when we're notified it's dead.
128                     // Meanwhile, we'd also need handle the case of zombie processes.
129                     mKillHandler.postDelayed(mProcKillTimer, this,
130                             mService.mConstants.mProcessKillTimeoutMs);
131                 }
132                 Process.killProcessQuiet(mPid);
133                 ProcessList.killProcessGroup(mUid, mPid);
134             }
135             mKilled = true;
136             Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
137         }
138     }
139 
140     private Runnable mProcKillTimer = new Runnable() {
141         @Override
142         public void run() {
143             synchronized (mLock) {
144                 // The process is maybe in either D or Z state.
145                 Slog.w(TAG, "Process " + toString() + " is still alive after "
146                         + mService.mConstants.mProcessKillTimeoutMs + "ms");
147                 // Force a cleanup as we can't keep the fd open forever
148                 mZombie = true;
149                 onProcDied(false);
150                 // But still bookkeep it, so it won't be added as a new one if it's spotted again.
151             }
152         }
153     };
154 
155     @GuardedBy("mLock")
updateAdjLocked()156     void updateAdjLocked() {
157         if (Process.readProcFile("/proc/" + mPid + "/oom_score_adj",
158                 LONG_FORMAT, null, LONG_OUT, null)) {
159             mAdj = (int) LONG_OUT[0];
160         }
161     }
162 
163     @GuardedBy("mLock")
onProcDied(boolean reallyDead)164     void onProcDied(boolean reallyDead) {
165         if (reallyDead) {
166             Slog.i(TAG, "Process " + toString() + " died");
167         }
168         mKillHandler.removeCallbacks(mProcKillTimer, this);
169         if (mOnKillListener != null) {
170             mOnKillListener.accept(this);
171         }
172     }
173 
174     @Override
toString()175     public String toString() {
176         if (mStringName != null) {
177             return mStringName;
178         }
179         StringBuilder sb = new StringBuilder(128);
180         sb.append("PhantomProcessRecord {");
181         sb.append(Integer.toHexString(System.identityHashCode(this)));
182         sb.append(' ');
183         sb.append(mPid);
184         sb.append(':');
185         sb.append(mPpid);
186         sb.append(':');
187         sb.append(mProcessName);
188         sb.append('/');
189         if (mUid < Process.FIRST_APPLICATION_UID) {
190             sb.append(mUid);
191         } else {
192             sb.append('u');
193             sb.append(UserHandle.getUserId(mUid));
194             int appId = UserHandle.getAppId(mUid);
195             if (appId >= Process.FIRST_APPLICATION_UID) {
196                 sb.append('a');
197                 sb.append(appId - Process.FIRST_APPLICATION_UID);
198             } else {
199                 sb.append('s');
200                 sb.append(appId);
201             }
202             if (appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID) {
203                 sb.append('i');
204                 sb.append(appId - Process.FIRST_ISOLATED_UID);
205             }
206         }
207         sb.append('}');
208         return mStringName = sb.toString();
209     }
210 
dump(PrintWriter pw, String prefix)211     void dump(PrintWriter pw, String prefix) {
212         final long now = SystemClock.elapsedRealtime();
213         pw.print(prefix);
214         pw.print("user #");
215         pw.print(UserHandle.getUserId(mUid));
216         pw.print(" uid=");
217         pw.print(mUid);
218         pw.print(" pid=");
219         pw.print(mPid);
220         pw.print(" ppid=");
221         pw.print(mPpid);
222         pw.print(" knownSince=");
223         TimeUtils.formatDuration(mKnownSince, now, pw);
224         pw.print(" killed=");
225         pw.println(mKilled);
226         pw.print(prefix);
227         pw.print("lastCpuTime=");
228         pw.print(mLastCputime);
229         if (mLastCputime > 0) {
230             pw.print(" timeUsed=");
231             TimeUtils.formatDuration(mCurrentCputime - mLastCputime, pw);
232         }
233         pw.print(" oom adj=");
234         pw.print(mAdj);
235         pw.print(" seq=");
236         pw.println(mUpdateSeq);
237     }
238 
equals(final String processName, final int uid, final int pid)239     boolean equals(final String processName, final int uid, final int pid) {
240         return mUid == uid && mPid == pid && TextUtils.equals(mProcessName, processName);
241     }
242 }
243