Loading services/core/java/com/android/server/am/LmkdConnection.java 0 → 100644 +300 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.am; import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR; import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; import android.net.LocalSocket; import android.net.LocalSocketAddress; import android.os.MessageQueue; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import libcore.io.IoUtils; import java.io.FileDescriptor; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; /** * Lmkd connection to communicate with lowmemorykiller daemon. */ public class LmkdConnection { private static final String TAG = TAG_WITH_CLASS_NAME ? "LmkdConnection" : TAG_AM; // lmkd reply max size in bytes private static final int LMKD_REPLY_MAX_SIZE = 12; // connection listener interface interface LmkdConnectionListener { boolean onConnect(OutputStream ostream); void onDisconnect(); /** * Check if received reply was expected (reply to an earlier request) * * @param replyBuf The buffer provided in exchange() to receive the reply. * It can be used by exchange() caller to store reply-specific * tags for later use in isReplyExpected() to verify if * received packet is the expected reply. * @param dataReceived The buffer holding received data * @param receivedLen Size of the data received */ boolean isReplyExpected(ByteBuffer replyBuf, ByteBuffer dataReceived, int receivedLen); /** * Handle the received message if it's unsolicited. * * @param dataReceived The buffer holding received data * @param receivedLen Size of the data received * @return True if the message has been handled correctly, false otherwise. */ boolean handleUnsolicitedMessage(ByteBuffer dataReceived, int receivedLen); } private final MessageQueue mMsgQueue; // lmkd connection listener private final LmkdConnectionListener mListener; // mutex to synchronize access to the socket private final Object mLmkdSocketLock = new Object(); // socket to communicate with lmkd @GuardedBy("mLmkdSocketLock") private LocalSocket mLmkdSocket = null; // socket I/O streams @GuardedBy("mLmkdSocketLock") private OutputStream mLmkdOutputStream = null; @GuardedBy("mLmkdSocketLock") private InputStream mLmkdInputStream = null; // buffer to store incoming data private final ByteBuffer mInputBuf = ByteBuffer.allocate(LMKD_REPLY_MAX_SIZE); // object to protect mReplyBuf and to wait/notify when reply is received private final Object mReplyBufLock = new Object(); // reply buffer @GuardedBy("mReplyBufLock") private ByteBuffer mReplyBuf = null; //////////////////// END FIELDS //////////////////// LmkdConnection(MessageQueue msgQueue, LmkdConnectionListener listener) { mMsgQueue = msgQueue; mListener = listener; } boolean connect() { synchronized (mLmkdSocketLock) { if (mLmkdSocket != null) { return true; } // temporary sockets and I/O streams final LocalSocket socket = openSocket(); if (socket == null) { Slog.w(TAG, "Failed to connect to lowmemorykiller, retry later"); return false; } final OutputStream ostream; final InputStream istream; try { ostream = socket.getOutputStream(); istream = socket.getInputStream(); } catch (IOException ex) { IoUtils.closeQuietly(socket); return false; } // execute onConnect callback if (mListener != null && !mListener.onConnect(ostream)) { Slog.w(TAG, "Failed to communicate with lowmemorykiller, retry later"); IoUtils.closeQuietly(socket); return false; } // connection established mLmkdSocket = socket; mLmkdOutputStream = ostream; mLmkdInputStream = istream; mMsgQueue.addOnFileDescriptorEventListener(mLmkdSocket.getFileDescriptor(), EVENT_INPUT | EVENT_ERROR, new MessageQueue.OnFileDescriptorEventListener() { public int onFileDescriptorEvents(FileDescriptor fd, int events) { return fileDescriptorEventHandler(fd, events); } } ); mLmkdSocketLock.notifyAll(); } return true; } private int fileDescriptorEventHandler(FileDescriptor fd, int events) { if (mListener == null) { return 0; } if ((events & EVENT_INPUT) != 0) { processIncomingData(); } if ((events & EVENT_ERROR) != 0) { synchronized (mLmkdSocketLock) { // stop listening on this socket mMsgQueue.removeOnFileDescriptorEventListener( mLmkdSocket.getFileDescriptor()); IoUtils.closeQuietly(mLmkdSocket); mLmkdSocket = null; } // wake up reply waiters if any synchronized (mReplyBufLock) { if (mReplyBuf != null) { mReplyBuf = null; mReplyBufLock.notifyAll(); } } // notify listener mListener.onDisconnect(); return 0; } return (EVENT_INPUT | EVENT_ERROR); } private void processIncomingData() { int len = read(mInputBuf); if (len > 0) { synchronized (mReplyBufLock) { if (mReplyBuf != null) { if (mListener.isReplyExpected(mReplyBuf, mInputBuf, len)) { // copy into reply buffer mReplyBuf.put(mInputBuf.array(), 0, len); mReplyBuf.rewind(); // wakeup the waiting thread mReplyBufLock.notifyAll(); } else if (!mListener.handleUnsolicitedMessage(mInputBuf, len)) { // received unexpected packet // treat this as an error mReplyBuf = null; mReplyBufLock.notifyAll(); Slog.e(TAG, "Received an unexpected packet from lmkd"); } } else if (!mListener.handleUnsolicitedMessage(mInputBuf, len)) { // received asynchronous communication from lmkd // but we don't recognize it. Slog.w(TAG, "Received an unexpected packet from lmkd"); } } } } boolean isConnected() { synchronized (mLmkdSocketLock) { return (mLmkdSocket != null); } } boolean waitForConnection(long timeoutMs) { synchronized (mLmkdSocketLock) { if (mLmkdSocket != null) { return true; } try { mLmkdSocketLock.wait(timeoutMs); return (mLmkdSocket != null); } catch (InterruptedException e) { return false; } } } private LocalSocket openSocket() { final LocalSocket socket; try { socket = new LocalSocket(LocalSocket.SOCKET_SEQPACKET); socket.connect( new LocalSocketAddress("lmkd", LocalSocketAddress.Namespace.RESERVED)); } catch (IOException ex) { Slog.e(TAG, "Connection failed: " + ex.toString()); return null; } return socket; } private boolean write(ByteBuffer buf) { synchronized (mLmkdSocketLock) { try { mLmkdOutputStream.write(buf.array(), 0, buf.position()); } catch (IOException ex) { return false; } return true; } } private int read(ByteBuffer buf) { synchronized (mLmkdSocketLock) { try { return mLmkdInputStream.read(buf.array(), 0, buf.array().length); } catch (IOException ex) { } return -1; } } /** * Exchange a request/reply packets with lmkd * * @param req The buffer holding the request data to be sent * @param repl The buffer to receive the reply */ public boolean exchange(ByteBuffer req, ByteBuffer repl) { if (repl == null) { return write(req); } boolean result = false; // set reply buffer to user-defined one to fill it synchronized (mReplyBufLock) { mReplyBuf = repl; if (write(req)) { try { // wait for the reply mReplyBufLock.wait(); result = (mReplyBuf != null); } catch (InterruptedException ie) { result = false; } } // reset reply buffer mReplyBuf = null; } return result; } } services/core/java/com/android/server/am/ProcessList.java +131 −90 Original line number Diff line number Diff line Loading @@ -57,8 +57,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.res.Resources; import android.graphics.Point; import android.net.LocalSocket; import android.net.LocalSocketAddress; import android.os.AppZygote; import android.os.Binder; import android.os.Build; Loading Loading @@ -103,11 +101,8 @@ import com.android.server.wm.WindowManagerService; import dalvik.system.VMRuntime; import libcore.io.IoUtils; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.nio.ByteBuffer; Loading @@ -118,11 +113,6 @@ import java.util.List; /** * Activity manager code dealing with processes. * * Method naming convention: * <ul> * <li> Methods suffixed with "LS" should be called within the {@link #sLmkdSocketLock} lock. * </ul> */ public final class ProcessList { static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessList" : TAG_AM; Loading Loading @@ -263,11 +253,16 @@ public final class ProcessList { // LMK_PROCREMOVE <pid> // LMK_PROCPURGE // LMK_GETKILLCNT // LMK_PROCKILL static final byte LMK_TARGET = 0; static final byte LMK_PROCPRIO = 1; static final byte LMK_PROCREMOVE = 2; static final byte LMK_PROCPURGE = 3; static final byte LMK_GETKILLCNT = 4; static final byte LMK_PROCKILL = 5; // Note: this is an unsolicated command // lmkd reconnect delay in msecs private static final long LMKD_RECONNECT_DELAY_MS = 1000; ActivityManagerService mService = null; Loading Loading @@ -303,16 +298,9 @@ public final class ProcessList { private boolean mHaveDisplaySize; private static Object sLmkdSocketLock = new Object(); private static LmkdConnection sLmkdConnection = null; @GuardedBy("sLmkdSocketLock") private static LocalSocket sLmkdSocket; @GuardedBy("sLmkdSocketLock") private static OutputStream sLmkdOutputStream; @GuardedBy("sLmkdSocketLock") private static InputStream sLmkdInputStream; private boolean mOomLevelsSet = false; /** * Temporary to avoid allocations. Protected by main lock. Loading Loading @@ -369,6 +357,12 @@ public final class ProcessList { ActiveUids mActiveUids; /** * The listener who is intereted with the lmkd kills. */ @GuardedBy("mService") private LmkdKillListener mLmkdKillListener = null; /** * The currently running isolated processes. */ Loading @@ -387,6 +381,13 @@ public final class ProcessList { private PlatformCompat mPlatformCompat = null; interface LmkdKillListener { /** * Called when there is a process kill by lmkd. */ void onLmkdKillOccurred(int pid, int uid); } final class IsolatedUidRange { @VisibleForTesting public final int mFirstUid; Loading Loading @@ -539,6 +540,8 @@ public final class ProcessList { final class KillHandler extends Handler { static final int KILL_PROCESS_GROUP_MSG = 4000; static final int LMKD_RECONNECT_MSG = 4001; static final int LMKD_PROC_KILLED_MSG = 4002; public KillHandler(Looper looper) { super(looper, null, true); Loading @@ -552,6 +555,18 @@ public final class ProcessList { Process.killProcessGroup(msg.arg1 /* uid */, msg.arg2 /* pid */); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; case LMKD_RECONNECT_MSG: if (!sLmkdConnection.connect()) { Slog.i(TAG, "Failed to connect to lmkd, retry after " + LMKD_RECONNECT_DELAY_MS + " ms"); // retry after LMKD_RECONNECT_DELAY_MS sKillHandler.sendMessageDelayed(sKillHandler.obtainMessage( KillHandler.LMKD_RECONNECT_MSG), LMKD_RECONNECT_DELAY_MS); } break; case LMKD_PROC_KILLED_MSG: handleLmkdProcKilled(msg.arg1 /* pid */, msg.arg2 /* uid */); break; default: super.handleMessage(msg); Loading Loading @@ -579,6 +594,50 @@ public final class ProcessList { THREAD_PRIORITY_BACKGROUND, true /* allowIo */); sKillThread.start(); sKillHandler = new KillHandler(sKillThread.getLooper()); sLmkdConnection = new LmkdConnection(sKillThread.getLooper().getQueue(), new LmkdConnection.LmkdConnectionListener() { @Override public boolean onConnect(OutputStream ostream) { Slog.i(TAG, "Connection with lmkd established"); return onLmkdConnect(ostream); } @Override public void onDisconnect() { Slog.w(TAG, "Lost connection to lmkd"); // start reconnection after delay to let lmkd restart sKillHandler.sendMessageDelayed(sKillHandler.obtainMessage( KillHandler.LMKD_RECONNECT_MSG), LMKD_RECONNECT_DELAY_MS); } @Override public boolean isReplyExpected(ByteBuffer replyBuf, ByteBuffer dataReceived, int receivedLen) { // compare the preambule (currently one integer) to check if // this is the reply packet we are waiting for return (receivedLen == replyBuf.array().length && dataReceived.getInt(0) == replyBuf.getInt(0)); } @Override public boolean handleUnsolicitedMessage(ByteBuffer dataReceived, int receivedLen) { if (receivedLen < 4) { return false; } switch (dataReceived.getInt(0)) { case LMK_PROCKILL: if (receivedLen != 12) { return false; } sKillHandler.obtainMessage(KillHandler.LMKD_PROC_KILLED_MSG, dataReceived.getInt(4), dataReceived.getInt(8)) .sendToTarget(); return true; default: return false; } } } ); } } Loading Loading @@ -684,6 +743,7 @@ public final class ProcessList { writeLmkd(buf, null); SystemProperties.set("sys.sysctl.extra_free_kbytes", Integer.toString(reserve)); mOomLevelsSet = true; } // GB: 2048,3072,4096,6144,7168,8192 // HC: 8192,10240,12288,14336,16384,20480 Loading Loading @@ -1223,93 +1283,50 @@ public final class ProcessList { buf.putInt(LMK_GETKILLCNT); buf.putInt(min_oom_adj); buf.putInt(max_oom_adj); if (writeLmkd(buf, repl)) { int i = repl.getInt(); if (i != LMK_GETKILLCNT) { Slog.e("ActivityManager", "Failed to get kill count, code mismatch"); return null; } // indicate what we are waiting for repl.putInt(LMK_GETKILLCNT); repl.rewind(); if (writeLmkd(buf, repl) && repl.getInt() == LMK_GETKILLCNT) { return new Integer(repl.getInt()); } return null; } @GuardedBy("sLmkdSocketLock") private static boolean openLmkdSocketLS() { boolean onLmkdConnect(OutputStream ostream) { try { sLmkdSocket = new LocalSocket(LocalSocket.SOCKET_SEQPACKET); sLmkdSocket.connect( new LocalSocketAddress("lmkd", LocalSocketAddress.Namespace.RESERVED)); sLmkdOutputStream = sLmkdSocket.getOutputStream(); sLmkdInputStream = sLmkdSocket.getInputStream(); } catch (IOException ex) { Slog.w(TAG, "lowmemorykiller daemon socket open failed"); sLmkdSocket = null; return false; // Purge any previously registered pids ByteBuffer buf = ByteBuffer.allocate(4); buf.putInt(LMK_PROCPURGE); ostream.write(buf.array(), 0, buf.position()); if (mOomLevelsSet) { // Reset oom_adj levels buf = ByteBuffer.allocate(4 * (2 * mOomAdj.length + 1)); buf.putInt(LMK_TARGET); for (int i = 0; i < mOomAdj.length; i++) { buf.putInt((mOomMinFree[i] * 1024) / PAGE_SIZE); buf.putInt(mOomAdj[i]); } return true; ostream.write(buf.array(), 0, buf.position()); } // Never call directly, use writeLmkd() instead @GuardedBy("sLmkdSocketLock") private static boolean writeLmkdCommandLS(ByteBuffer buf) { try { sLmkdOutputStream.write(buf.array(), 0, buf.position()); } catch (IOException ex) { Slog.w(TAG, "Error writing to lowmemorykiller socket"); IoUtils.closeQuietly(sLmkdSocket); sLmkdSocket = null; return false; } return true; } // Never call directly, use writeLmkd() instead @GuardedBy("sLmkdSocketLock") private static boolean readLmkdReplyLS(ByteBuffer buf) { int len; try { len = sLmkdInputStream.read(buf.array(), 0, buf.array().length); if (len == buf.array().length) { return true; } } catch (IOException ex) { Slog.w(TAG, "Error reading from lowmemorykiller socket"); } private static boolean writeLmkd(ByteBuffer buf, ByteBuffer repl) { if (!sLmkdConnection.isConnected()) { // try to connect immediately and then keep retrying sKillHandler.sendMessage( sKillHandler.obtainMessage(KillHandler.LMKD_RECONNECT_MSG)); IoUtils.closeQuietly(sLmkdSocket); sLmkdSocket = null; // wait for connection retrying 3 times (up to 3 seconds) if (!sLmkdConnection.waitForConnection(3 * LMKD_RECONNECT_DELAY_MS)) { return false; } private static boolean writeLmkd(ByteBuffer buf, ByteBuffer repl) { synchronized (sLmkdSocketLock) { for (int i = 0; i < 3; i++) { if (sLmkdSocket == null) { if (openLmkdSocketLS() == false) { try { Thread.sleep(1000); } catch (InterruptedException ie) { } continue; } // Purge any previously registered pids ByteBuffer purge_buf = ByteBuffer.allocate(4); purge_buf.putInt(LMK_PROCPURGE); if (writeLmkdCommandLS(purge_buf) == false) { // Write failed, skip the rest and retry continue; } } if (writeLmkdCommandLS(buf) && (repl == null || readLmkdReplyLS(repl))) { return true; } } } return false; return sLmkdConnection.exchange(buf, repl); } static void killProcessGroup(int uid, int pid) { Loading Loading @@ -3214,4 +3231,28 @@ public final class ProcessList { mService.doStopUidLocked(uidRec.uid, uidRec); } } void setLmkdKillListener(final LmkdKillListener listener) { synchronized (mService) { mLmkdKillListener = listener; } } private void handleLmkdProcKilled(final int pid, final int uid) { // Log only now if (DEBUG_PROCESSES) { Slog.i(TAG, "lmkd kill: pid=" + pid + " uid=" + uid); } if (mService == null) { return; } // Notify any interesed party regarding the lmkd kills synchronized (mService) { final LmkdKillListener listener = mLmkdKillListener; if (listener != null) { mService.mHandler.post(()-> listener.onLmkdKillOccurred(pid, uid)); } } } } Loading
services/core/java/com/android/server/am/LmkdConnection.java 0 → 100644 +300 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.am; import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR; import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; import android.net.LocalSocket; import android.net.LocalSocketAddress; import android.os.MessageQueue; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import libcore.io.IoUtils; import java.io.FileDescriptor; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; /** * Lmkd connection to communicate with lowmemorykiller daemon. */ public class LmkdConnection { private static final String TAG = TAG_WITH_CLASS_NAME ? "LmkdConnection" : TAG_AM; // lmkd reply max size in bytes private static final int LMKD_REPLY_MAX_SIZE = 12; // connection listener interface interface LmkdConnectionListener { boolean onConnect(OutputStream ostream); void onDisconnect(); /** * Check if received reply was expected (reply to an earlier request) * * @param replyBuf The buffer provided in exchange() to receive the reply. * It can be used by exchange() caller to store reply-specific * tags for later use in isReplyExpected() to verify if * received packet is the expected reply. * @param dataReceived The buffer holding received data * @param receivedLen Size of the data received */ boolean isReplyExpected(ByteBuffer replyBuf, ByteBuffer dataReceived, int receivedLen); /** * Handle the received message if it's unsolicited. * * @param dataReceived The buffer holding received data * @param receivedLen Size of the data received * @return True if the message has been handled correctly, false otherwise. */ boolean handleUnsolicitedMessage(ByteBuffer dataReceived, int receivedLen); } private final MessageQueue mMsgQueue; // lmkd connection listener private final LmkdConnectionListener mListener; // mutex to synchronize access to the socket private final Object mLmkdSocketLock = new Object(); // socket to communicate with lmkd @GuardedBy("mLmkdSocketLock") private LocalSocket mLmkdSocket = null; // socket I/O streams @GuardedBy("mLmkdSocketLock") private OutputStream mLmkdOutputStream = null; @GuardedBy("mLmkdSocketLock") private InputStream mLmkdInputStream = null; // buffer to store incoming data private final ByteBuffer mInputBuf = ByteBuffer.allocate(LMKD_REPLY_MAX_SIZE); // object to protect mReplyBuf and to wait/notify when reply is received private final Object mReplyBufLock = new Object(); // reply buffer @GuardedBy("mReplyBufLock") private ByteBuffer mReplyBuf = null; //////////////////// END FIELDS //////////////////// LmkdConnection(MessageQueue msgQueue, LmkdConnectionListener listener) { mMsgQueue = msgQueue; mListener = listener; } boolean connect() { synchronized (mLmkdSocketLock) { if (mLmkdSocket != null) { return true; } // temporary sockets and I/O streams final LocalSocket socket = openSocket(); if (socket == null) { Slog.w(TAG, "Failed to connect to lowmemorykiller, retry later"); return false; } final OutputStream ostream; final InputStream istream; try { ostream = socket.getOutputStream(); istream = socket.getInputStream(); } catch (IOException ex) { IoUtils.closeQuietly(socket); return false; } // execute onConnect callback if (mListener != null && !mListener.onConnect(ostream)) { Slog.w(TAG, "Failed to communicate with lowmemorykiller, retry later"); IoUtils.closeQuietly(socket); return false; } // connection established mLmkdSocket = socket; mLmkdOutputStream = ostream; mLmkdInputStream = istream; mMsgQueue.addOnFileDescriptorEventListener(mLmkdSocket.getFileDescriptor(), EVENT_INPUT | EVENT_ERROR, new MessageQueue.OnFileDescriptorEventListener() { public int onFileDescriptorEvents(FileDescriptor fd, int events) { return fileDescriptorEventHandler(fd, events); } } ); mLmkdSocketLock.notifyAll(); } return true; } private int fileDescriptorEventHandler(FileDescriptor fd, int events) { if (mListener == null) { return 0; } if ((events & EVENT_INPUT) != 0) { processIncomingData(); } if ((events & EVENT_ERROR) != 0) { synchronized (mLmkdSocketLock) { // stop listening on this socket mMsgQueue.removeOnFileDescriptorEventListener( mLmkdSocket.getFileDescriptor()); IoUtils.closeQuietly(mLmkdSocket); mLmkdSocket = null; } // wake up reply waiters if any synchronized (mReplyBufLock) { if (mReplyBuf != null) { mReplyBuf = null; mReplyBufLock.notifyAll(); } } // notify listener mListener.onDisconnect(); return 0; } return (EVENT_INPUT | EVENT_ERROR); } private void processIncomingData() { int len = read(mInputBuf); if (len > 0) { synchronized (mReplyBufLock) { if (mReplyBuf != null) { if (mListener.isReplyExpected(mReplyBuf, mInputBuf, len)) { // copy into reply buffer mReplyBuf.put(mInputBuf.array(), 0, len); mReplyBuf.rewind(); // wakeup the waiting thread mReplyBufLock.notifyAll(); } else if (!mListener.handleUnsolicitedMessage(mInputBuf, len)) { // received unexpected packet // treat this as an error mReplyBuf = null; mReplyBufLock.notifyAll(); Slog.e(TAG, "Received an unexpected packet from lmkd"); } } else if (!mListener.handleUnsolicitedMessage(mInputBuf, len)) { // received asynchronous communication from lmkd // but we don't recognize it. Slog.w(TAG, "Received an unexpected packet from lmkd"); } } } } boolean isConnected() { synchronized (mLmkdSocketLock) { return (mLmkdSocket != null); } } boolean waitForConnection(long timeoutMs) { synchronized (mLmkdSocketLock) { if (mLmkdSocket != null) { return true; } try { mLmkdSocketLock.wait(timeoutMs); return (mLmkdSocket != null); } catch (InterruptedException e) { return false; } } } private LocalSocket openSocket() { final LocalSocket socket; try { socket = new LocalSocket(LocalSocket.SOCKET_SEQPACKET); socket.connect( new LocalSocketAddress("lmkd", LocalSocketAddress.Namespace.RESERVED)); } catch (IOException ex) { Slog.e(TAG, "Connection failed: " + ex.toString()); return null; } return socket; } private boolean write(ByteBuffer buf) { synchronized (mLmkdSocketLock) { try { mLmkdOutputStream.write(buf.array(), 0, buf.position()); } catch (IOException ex) { return false; } return true; } } private int read(ByteBuffer buf) { synchronized (mLmkdSocketLock) { try { return mLmkdInputStream.read(buf.array(), 0, buf.array().length); } catch (IOException ex) { } return -1; } } /** * Exchange a request/reply packets with lmkd * * @param req The buffer holding the request data to be sent * @param repl The buffer to receive the reply */ public boolean exchange(ByteBuffer req, ByteBuffer repl) { if (repl == null) { return write(req); } boolean result = false; // set reply buffer to user-defined one to fill it synchronized (mReplyBufLock) { mReplyBuf = repl; if (write(req)) { try { // wait for the reply mReplyBufLock.wait(); result = (mReplyBuf != null); } catch (InterruptedException ie) { result = false; } } // reset reply buffer mReplyBuf = null; } return result; } }
services/core/java/com/android/server/am/ProcessList.java +131 −90 Original line number Diff line number Diff line Loading @@ -57,8 +57,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.res.Resources; import android.graphics.Point; import android.net.LocalSocket; import android.net.LocalSocketAddress; import android.os.AppZygote; import android.os.Binder; import android.os.Build; Loading Loading @@ -103,11 +101,8 @@ import com.android.server.wm.WindowManagerService; import dalvik.system.VMRuntime; import libcore.io.IoUtils; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.nio.ByteBuffer; Loading @@ -118,11 +113,6 @@ import java.util.List; /** * Activity manager code dealing with processes. * * Method naming convention: * <ul> * <li> Methods suffixed with "LS" should be called within the {@link #sLmkdSocketLock} lock. * </ul> */ public final class ProcessList { static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessList" : TAG_AM; Loading Loading @@ -263,11 +253,16 @@ public final class ProcessList { // LMK_PROCREMOVE <pid> // LMK_PROCPURGE // LMK_GETKILLCNT // LMK_PROCKILL static final byte LMK_TARGET = 0; static final byte LMK_PROCPRIO = 1; static final byte LMK_PROCREMOVE = 2; static final byte LMK_PROCPURGE = 3; static final byte LMK_GETKILLCNT = 4; static final byte LMK_PROCKILL = 5; // Note: this is an unsolicated command // lmkd reconnect delay in msecs private static final long LMKD_RECONNECT_DELAY_MS = 1000; ActivityManagerService mService = null; Loading Loading @@ -303,16 +298,9 @@ public final class ProcessList { private boolean mHaveDisplaySize; private static Object sLmkdSocketLock = new Object(); private static LmkdConnection sLmkdConnection = null; @GuardedBy("sLmkdSocketLock") private static LocalSocket sLmkdSocket; @GuardedBy("sLmkdSocketLock") private static OutputStream sLmkdOutputStream; @GuardedBy("sLmkdSocketLock") private static InputStream sLmkdInputStream; private boolean mOomLevelsSet = false; /** * Temporary to avoid allocations. Protected by main lock. Loading Loading @@ -369,6 +357,12 @@ public final class ProcessList { ActiveUids mActiveUids; /** * The listener who is intereted with the lmkd kills. */ @GuardedBy("mService") private LmkdKillListener mLmkdKillListener = null; /** * The currently running isolated processes. */ Loading @@ -387,6 +381,13 @@ public final class ProcessList { private PlatformCompat mPlatformCompat = null; interface LmkdKillListener { /** * Called when there is a process kill by lmkd. */ void onLmkdKillOccurred(int pid, int uid); } final class IsolatedUidRange { @VisibleForTesting public final int mFirstUid; Loading Loading @@ -539,6 +540,8 @@ public final class ProcessList { final class KillHandler extends Handler { static final int KILL_PROCESS_GROUP_MSG = 4000; static final int LMKD_RECONNECT_MSG = 4001; static final int LMKD_PROC_KILLED_MSG = 4002; public KillHandler(Looper looper) { super(looper, null, true); Loading @@ -552,6 +555,18 @@ public final class ProcessList { Process.killProcessGroup(msg.arg1 /* uid */, msg.arg2 /* pid */); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; case LMKD_RECONNECT_MSG: if (!sLmkdConnection.connect()) { Slog.i(TAG, "Failed to connect to lmkd, retry after " + LMKD_RECONNECT_DELAY_MS + " ms"); // retry after LMKD_RECONNECT_DELAY_MS sKillHandler.sendMessageDelayed(sKillHandler.obtainMessage( KillHandler.LMKD_RECONNECT_MSG), LMKD_RECONNECT_DELAY_MS); } break; case LMKD_PROC_KILLED_MSG: handleLmkdProcKilled(msg.arg1 /* pid */, msg.arg2 /* uid */); break; default: super.handleMessage(msg); Loading Loading @@ -579,6 +594,50 @@ public final class ProcessList { THREAD_PRIORITY_BACKGROUND, true /* allowIo */); sKillThread.start(); sKillHandler = new KillHandler(sKillThread.getLooper()); sLmkdConnection = new LmkdConnection(sKillThread.getLooper().getQueue(), new LmkdConnection.LmkdConnectionListener() { @Override public boolean onConnect(OutputStream ostream) { Slog.i(TAG, "Connection with lmkd established"); return onLmkdConnect(ostream); } @Override public void onDisconnect() { Slog.w(TAG, "Lost connection to lmkd"); // start reconnection after delay to let lmkd restart sKillHandler.sendMessageDelayed(sKillHandler.obtainMessage( KillHandler.LMKD_RECONNECT_MSG), LMKD_RECONNECT_DELAY_MS); } @Override public boolean isReplyExpected(ByteBuffer replyBuf, ByteBuffer dataReceived, int receivedLen) { // compare the preambule (currently one integer) to check if // this is the reply packet we are waiting for return (receivedLen == replyBuf.array().length && dataReceived.getInt(0) == replyBuf.getInt(0)); } @Override public boolean handleUnsolicitedMessage(ByteBuffer dataReceived, int receivedLen) { if (receivedLen < 4) { return false; } switch (dataReceived.getInt(0)) { case LMK_PROCKILL: if (receivedLen != 12) { return false; } sKillHandler.obtainMessage(KillHandler.LMKD_PROC_KILLED_MSG, dataReceived.getInt(4), dataReceived.getInt(8)) .sendToTarget(); return true; default: return false; } } } ); } } Loading Loading @@ -684,6 +743,7 @@ public final class ProcessList { writeLmkd(buf, null); SystemProperties.set("sys.sysctl.extra_free_kbytes", Integer.toString(reserve)); mOomLevelsSet = true; } // GB: 2048,3072,4096,6144,7168,8192 // HC: 8192,10240,12288,14336,16384,20480 Loading Loading @@ -1223,93 +1283,50 @@ public final class ProcessList { buf.putInt(LMK_GETKILLCNT); buf.putInt(min_oom_adj); buf.putInt(max_oom_adj); if (writeLmkd(buf, repl)) { int i = repl.getInt(); if (i != LMK_GETKILLCNT) { Slog.e("ActivityManager", "Failed to get kill count, code mismatch"); return null; } // indicate what we are waiting for repl.putInt(LMK_GETKILLCNT); repl.rewind(); if (writeLmkd(buf, repl) && repl.getInt() == LMK_GETKILLCNT) { return new Integer(repl.getInt()); } return null; } @GuardedBy("sLmkdSocketLock") private static boolean openLmkdSocketLS() { boolean onLmkdConnect(OutputStream ostream) { try { sLmkdSocket = new LocalSocket(LocalSocket.SOCKET_SEQPACKET); sLmkdSocket.connect( new LocalSocketAddress("lmkd", LocalSocketAddress.Namespace.RESERVED)); sLmkdOutputStream = sLmkdSocket.getOutputStream(); sLmkdInputStream = sLmkdSocket.getInputStream(); } catch (IOException ex) { Slog.w(TAG, "lowmemorykiller daemon socket open failed"); sLmkdSocket = null; return false; // Purge any previously registered pids ByteBuffer buf = ByteBuffer.allocate(4); buf.putInt(LMK_PROCPURGE); ostream.write(buf.array(), 0, buf.position()); if (mOomLevelsSet) { // Reset oom_adj levels buf = ByteBuffer.allocate(4 * (2 * mOomAdj.length + 1)); buf.putInt(LMK_TARGET); for (int i = 0; i < mOomAdj.length; i++) { buf.putInt((mOomMinFree[i] * 1024) / PAGE_SIZE); buf.putInt(mOomAdj[i]); } return true; ostream.write(buf.array(), 0, buf.position()); } // Never call directly, use writeLmkd() instead @GuardedBy("sLmkdSocketLock") private static boolean writeLmkdCommandLS(ByteBuffer buf) { try { sLmkdOutputStream.write(buf.array(), 0, buf.position()); } catch (IOException ex) { Slog.w(TAG, "Error writing to lowmemorykiller socket"); IoUtils.closeQuietly(sLmkdSocket); sLmkdSocket = null; return false; } return true; } // Never call directly, use writeLmkd() instead @GuardedBy("sLmkdSocketLock") private static boolean readLmkdReplyLS(ByteBuffer buf) { int len; try { len = sLmkdInputStream.read(buf.array(), 0, buf.array().length); if (len == buf.array().length) { return true; } } catch (IOException ex) { Slog.w(TAG, "Error reading from lowmemorykiller socket"); } private static boolean writeLmkd(ByteBuffer buf, ByteBuffer repl) { if (!sLmkdConnection.isConnected()) { // try to connect immediately and then keep retrying sKillHandler.sendMessage( sKillHandler.obtainMessage(KillHandler.LMKD_RECONNECT_MSG)); IoUtils.closeQuietly(sLmkdSocket); sLmkdSocket = null; // wait for connection retrying 3 times (up to 3 seconds) if (!sLmkdConnection.waitForConnection(3 * LMKD_RECONNECT_DELAY_MS)) { return false; } private static boolean writeLmkd(ByteBuffer buf, ByteBuffer repl) { synchronized (sLmkdSocketLock) { for (int i = 0; i < 3; i++) { if (sLmkdSocket == null) { if (openLmkdSocketLS() == false) { try { Thread.sleep(1000); } catch (InterruptedException ie) { } continue; } // Purge any previously registered pids ByteBuffer purge_buf = ByteBuffer.allocate(4); purge_buf.putInt(LMK_PROCPURGE); if (writeLmkdCommandLS(purge_buf) == false) { // Write failed, skip the rest and retry continue; } } if (writeLmkdCommandLS(buf) && (repl == null || readLmkdReplyLS(repl))) { return true; } } } return false; return sLmkdConnection.exchange(buf, repl); } static void killProcessGroup(int uid, int pid) { Loading Loading @@ -3214,4 +3231,28 @@ public final class ProcessList { mService.doStopUidLocked(uidRec.uid, uidRec); } } void setLmkdKillListener(final LmkdKillListener listener) { synchronized (mService) { mLmkdKillListener = listener; } } private void handleLmkdProcKilled(final int pid, final int uid) { // Log only now if (DEBUG_PROCESSES) { Slog.i(TAG, "lmkd kill: pid=" + pid + " uid=" + uid); } if (mService == null) { return; } // Notify any interesed party regarding the lmkd kills synchronized (mService) { final LmkdKillListener listener = mLmkdKillListener; if (listener != null) { mService.mHandler.post(()-> listener.onLmkdKillOccurred(pid, uid)); } } } }