From 80bf49d556ff7baafc9fee07926605fbcadc2c2f Mon Sep 17 00:00:00 2001 From: Suren Baghdasaryan Date: Fri, 15 Mar 2019 15:06:54 -0700 Subject: [PATCH 1/2] Implement ActivityManager to LMKD reconnect functionality In case of LMKD crashing and restarting or for testing purposes when LMKD is killed and restarted on purpose, ActivityManager needs to reconnect and reset the oom_adj levels so that LMKD can function. Implement a separate LmkdConnection class handling LMKD connection and communication. We use FileDescriptorEventListener to detect LMKD connection loss and retry logic is implemented to reconnect and reset LMKD parameters. Bug: 124618999 Test: kill lmkd and verify successful reconnect Change-Id: I1b63c960aa9580cdc4f8d3697cd02fa9c18a2935 Merged-In: I1b63c960aa9580cdc4f8d3697cd02fa9c18a2935 Signed-off-by: Suren Baghdasaryan --- .../com/android/server/am/LmkdConnection.java | 291 ++++++++++++++++++ .../com/android/server/am/ProcessList.java | 160 +++++----- 2 files changed, 360 insertions(+), 91 deletions(-) create mode 100644 services/core/java/com/android/server/am/LmkdConnection.java diff --git a/services/core/java/com/android/server/am/LmkdConnection.java b/services/core/java/com/android/server/am/LmkdConnection.java new file mode 100644 index 0000000000000..0a2f608619f16 --- /dev/null +++ b/services/core/java/com/android/server/am/LmkdConnection.java @@ -0,0 +1,291 @@ +/* + * 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 = 8; + + // 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); + } + + 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 { + // received asynchronous or unexpected packet + // treat this as an error + mReplyBuf = null; + mReplyBufLock.notifyAll(); + Slog.e(TAG, "Received unexpected packet from lmkd"); + } + } else { + // received asynchronous communication from lmkd + // we don't support this yet + Slog.w(TAG, "Received an asynchronous 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; + } +} diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index f397a3243b2a2..197829a075c4f 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -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; @@ -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; @@ -118,11 +113,6 @@ import java.util.List; /** * Activity manager code dealing with processes. - * - * Method naming convention: - * */ public final class ProcessList { static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessList" : TAG_AM; @@ -269,6 +259,9 @@ public final class ProcessList { static final byte LMK_PROCPURGE = 3; static final byte LMK_GETKILLCNT = 4; + // lmkd reconnect delay in msecs + private static final long LMDK_RECONNECT_DELAY_MS = 1000; + ActivityManagerService mService = null; // To kill process groups asynchronously @@ -303,16 +296,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. @@ -539,6 +525,7 @@ public final class ProcessList { final class KillHandler extends Handler { static final int KILL_PROCESS_GROUP_MSG = 4000; + static final int LMDK_RECONNECT_MSG = 4001; public KillHandler(Looper looper) { super(looper, null, true); @@ -552,6 +539,15 @@ public final class ProcessList { Process.killProcessGroup(msg.arg1 /* uid */, msg.arg2 /* pid */); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; + case LMDK_RECONNECT_MSG: + if (!sLmkdConnection.connect()) { + Slog.i(TAG, "Failed to connect to lmkd, retry after " + + LMDK_RECONNECT_DELAY_MS + " ms"); + // retry after LMDK_RECONNECT_DELAY_MS + sKillHandler.sendMessageDelayed(sKillHandler.obtainMessage( + KillHandler.LMDK_RECONNECT_MSG), LMDK_RECONNECT_DELAY_MS); + } + break; default: super.handleMessage(msg); @@ -579,6 +575,30 @@ 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.LMDK_RECONNECT_MSG), LMDK_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)); + } + } + ); } } @@ -684,6 +704,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 @@ -1223,93 +1244,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; - } - - return true; - } - - // 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; + // 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]); + } + ostream.write(buf.array(), 0, buf.position()); } } catch (IOException ex) { - Slog.w(TAG, "Error reading from lowmemorykiller socket"); + return false; } - - IoUtils.closeQuietly(sLmkdSocket); - sLmkdSocket = null; - return false; + return true; } 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; - } + if (!sLmkdConnection.isConnected()) { + // try to connect immediately and then keep retrying + sKillHandler.sendMessage( + sKillHandler.obtainMessage(KillHandler.LMDK_RECONNECT_MSG)); - // 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; - } + // wait for connection retrying 3 times (up to 3 seconds) + if (!sLmkdConnection.waitForConnection(3 * LMDK_RECONNECT_DELAY_MS)) { + return false; } } - return false; + + return sLmkdConnection.exchange(buf, repl); } static void killProcessGroup(int uid, int pid) { From 26985845dd94ba50624b5d74d053c23a7be4f865 Mon Sep 17 00:00:00 2001 From: Jing Ji Date: Thu, 5 Dec 2019 17:24:04 -0800 Subject: [PATCH 2/2] Support unsolicited message from lmkd Add ability in AMS to receive notifications from LMKD Bug: 136036078 Test: memeater 2048 Change-Id: Id87e95f4f989a37586fec02d239bd0e527817f47 Merged-In: If183c177bb32bad18373dfcd8434e48313935a4c --- .../com/android/server/am/LmkdConnection.java | 23 ++++-- .../com/android/server/am/ProcessList.java | 81 ++++++++++++++++--- 2 files changed, 88 insertions(+), 16 deletions(-) diff --git a/services/core/java/com/android/server/am/LmkdConnection.java b/services/core/java/com/android/server/am/LmkdConnection.java index 0a2f608619f16..cacf6765a28e2 100644 --- a/services/core/java/com/android/server/am/LmkdConnection.java +++ b/services/core/java/com/android/server/am/LmkdConnection.java @@ -44,7 +44,7 @@ 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 = 8; + private static final int LMKD_REPLY_MAX_SIZE = 12; // connection listener interface interface LmkdConnectionListener { @@ -62,6 +62,15 @@ public class LmkdConnection { */ 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; @@ -185,17 +194,17 @@ public class LmkdConnection { mReplyBuf.rewind(); // wakeup the waiting thread mReplyBufLock.notifyAll(); - } else { - // received asynchronous or unexpected packet + } else if (!mListener.handleUnsolicitedMessage(mInputBuf, len)) { + // received unexpected packet // treat this as an error mReplyBuf = null; mReplyBufLock.notifyAll(); - Slog.e(TAG, "Received unexpected packet from lmkd"); + Slog.e(TAG, "Received an unexpected packet from lmkd"); } - } else { + } else if (!mListener.handleUnsolicitedMessage(mInputBuf, len)) { // received asynchronous communication from lmkd - // we don't support this yet - Slog.w(TAG, "Received an asynchronous packet from lmkd"); + // but we don't recognize it. + Slog.w(TAG, "Received an unexpected packet from lmkd"); } } } diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 197829a075c4f..3b8d39bf7a19b 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -253,14 +253,16 @@ public final class ProcessList { // LMK_PROCREMOVE // 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 LMDK_RECONNECT_DELAY_MS = 1000; + private static final long LMKD_RECONNECT_DELAY_MS = 1000; ActivityManagerService mService = null; @@ -355,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. */ @@ -373,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; @@ -525,7 +540,8 @@ public final class ProcessList { final class KillHandler extends Handler { static final int KILL_PROCESS_GROUP_MSG = 4000; - static final int LMDK_RECONNECT_MSG = 4001; + static final int LMKD_RECONNECT_MSG = 4001; + static final int LMKD_PROC_KILLED_MSG = 4002; public KillHandler(Looper looper) { super(looper, null, true); @@ -539,15 +555,18 @@ public final class ProcessList { Process.killProcessGroup(msg.arg1 /* uid */, msg.arg2 /* pid */); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; - case LMDK_RECONNECT_MSG: + case LMKD_RECONNECT_MSG: if (!sLmkdConnection.connect()) { Slog.i(TAG, "Failed to connect to lmkd, retry after " - + LMDK_RECONNECT_DELAY_MS + " ms"); - // retry after LMDK_RECONNECT_DELAY_MS + + LMKD_RECONNECT_DELAY_MS + " ms"); + // retry after LMKD_RECONNECT_DELAY_MS sKillHandler.sendMessageDelayed(sKillHandler.obtainMessage( - KillHandler.LMDK_RECONNECT_MSG), LMDK_RECONNECT_DELAY_MS); + 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); @@ -587,7 +606,7 @@ public final class ProcessList { Slog.w(TAG, "Lost connection to lmkd"); // start reconnection after delay to let lmkd restart sKillHandler.sendMessageDelayed(sKillHandler.obtainMessage( - KillHandler.LMDK_RECONNECT_MSG), LMDK_RECONNECT_DELAY_MS); + KillHandler.LMKD_RECONNECT_MSG), LMKD_RECONNECT_DELAY_MS); } @Override public boolean isReplyExpected(ByteBuffer replyBuf, @@ -597,6 +616,26 @@ public final class ProcessList { 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; + } + } } ); } @@ -1279,10 +1318,10 @@ public final class ProcessList { if (!sLmkdConnection.isConnected()) { // try to connect immediately and then keep retrying sKillHandler.sendMessage( - sKillHandler.obtainMessage(KillHandler.LMDK_RECONNECT_MSG)); + sKillHandler.obtainMessage(KillHandler.LMKD_RECONNECT_MSG)); // wait for connection retrying 3 times (up to 3 seconds) - if (!sLmkdConnection.waitForConnection(3 * LMDK_RECONNECT_DELAY_MS)) { + if (!sLmkdConnection.waitForConnection(3 * LMKD_RECONNECT_DELAY_MS)) { return false; } } @@ -3192,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)); + } + } + } }