Merge changes from topic "lmkd_async_msg"
* changes: Support unsolicited message from lmkd Implement ActivityManager to LMKD reconnect functionality
This commit is contained in:
300
services/core/java/com/android/server/am/LmkdConnection.java
Normal file
300
services/core/java/com/android/server/am/LmkdConnection.java
Normal file
@@ -0,0 +1,300 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -57,8 +57,6 @@ import android.content.pm.ApplicationInfo;
|
|||||||
import android.content.pm.IPackageManager;
|
import android.content.pm.IPackageManager;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
import android.net.LocalSocket;
|
|
||||||
import android.net.LocalSocketAddress;
|
|
||||||
import android.os.AppZygote;
|
import android.os.AppZygote;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
@@ -103,11 +101,8 @@ import com.android.server.wm.WindowManagerService;
|
|||||||
|
|
||||||
import dalvik.system.VMRuntime;
|
import dalvik.system.VMRuntime;
|
||||||
|
|
||||||
import libcore.io.IoUtils;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
@@ -118,11 +113,6 @@ import java.util.List;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Activity manager code dealing with processes.
|
* 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 {
|
public final class ProcessList {
|
||||||
static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessList" : TAG_AM;
|
static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessList" : TAG_AM;
|
||||||
@@ -263,11 +253,16 @@ public final class ProcessList {
|
|||||||
// LMK_PROCREMOVE <pid>
|
// LMK_PROCREMOVE <pid>
|
||||||
// LMK_PROCPURGE
|
// LMK_PROCPURGE
|
||||||
// LMK_GETKILLCNT
|
// LMK_GETKILLCNT
|
||||||
|
// LMK_PROCKILL
|
||||||
static final byte LMK_TARGET = 0;
|
static final byte LMK_TARGET = 0;
|
||||||
static final byte LMK_PROCPRIO = 1;
|
static final byte LMK_PROCPRIO = 1;
|
||||||
static final byte LMK_PROCREMOVE = 2;
|
static final byte LMK_PROCREMOVE = 2;
|
||||||
static final byte LMK_PROCPURGE = 3;
|
static final byte LMK_PROCPURGE = 3;
|
||||||
static final byte LMK_GETKILLCNT = 4;
|
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;
|
ActivityManagerService mService = null;
|
||||||
|
|
||||||
@@ -303,16 +298,9 @@ public final class ProcessList {
|
|||||||
|
|
||||||
private boolean mHaveDisplaySize;
|
private boolean mHaveDisplaySize;
|
||||||
|
|
||||||
private static Object sLmkdSocketLock = new Object();
|
private static LmkdConnection sLmkdConnection = null;
|
||||||
|
|
||||||
@GuardedBy("sLmkdSocketLock")
|
private boolean mOomLevelsSet = false;
|
||||||
private static LocalSocket sLmkdSocket;
|
|
||||||
|
|
||||||
@GuardedBy("sLmkdSocketLock")
|
|
||||||
private static OutputStream sLmkdOutputStream;
|
|
||||||
|
|
||||||
@GuardedBy("sLmkdSocketLock")
|
|
||||||
private static InputStream sLmkdInputStream;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Temporary to avoid allocations. Protected by main lock.
|
* Temporary to avoid allocations. Protected by main lock.
|
||||||
@@ -369,6 +357,12 @@ public final class ProcessList {
|
|||||||
|
|
||||||
ActiveUids mActiveUids;
|
ActiveUids mActiveUids;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The listener who is intereted with the lmkd kills.
|
||||||
|
*/
|
||||||
|
@GuardedBy("mService")
|
||||||
|
private LmkdKillListener mLmkdKillListener = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The currently running isolated processes.
|
* The currently running isolated processes.
|
||||||
*/
|
*/
|
||||||
@@ -387,6 +381,13 @@ public final class ProcessList {
|
|||||||
|
|
||||||
private PlatformCompat mPlatformCompat = null;
|
private PlatformCompat mPlatformCompat = null;
|
||||||
|
|
||||||
|
interface LmkdKillListener {
|
||||||
|
/**
|
||||||
|
* Called when there is a process kill by lmkd.
|
||||||
|
*/
|
||||||
|
void onLmkdKillOccurred(int pid, int uid);
|
||||||
|
}
|
||||||
|
|
||||||
final class IsolatedUidRange {
|
final class IsolatedUidRange {
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public final int mFirstUid;
|
public final int mFirstUid;
|
||||||
@@ -539,6 +540,8 @@ public final class ProcessList {
|
|||||||
|
|
||||||
final class KillHandler extends Handler {
|
final class KillHandler extends Handler {
|
||||||
static final int KILL_PROCESS_GROUP_MSG = 4000;
|
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) {
|
public KillHandler(Looper looper) {
|
||||||
super(looper, null, true);
|
super(looper, null, true);
|
||||||
@@ -552,6 +555,18 @@ public final class ProcessList {
|
|||||||
Process.killProcessGroup(msg.arg1 /* uid */, msg.arg2 /* pid */);
|
Process.killProcessGroup(msg.arg1 /* uid */, msg.arg2 /* pid */);
|
||||||
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
|
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
|
||||||
break;
|
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:
|
default:
|
||||||
super.handleMessage(msg);
|
super.handleMessage(msg);
|
||||||
@@ -579,6 +594,50 @@ public final class ProcessList {
|
|||||||
THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
|
THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
|
||||||
sKillThread.start();
|
sKillThread.start();
|
||||||
sKillHandler = new KillHandler(sKillThread.getLooper());
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -684,6 +743,7 @@ public final class ProcessList {
|
|||||||
|
|
||||||
writeLmkd(buf, null);
|
writeLmkd(buf, null);
|
||||||
SystemProperties.set("sys.sysctl.extra_free_kbytes", Integer.toString(reserve));
|
SystemProperties.set("sys.sysctl.extra_free_kbytes", Integer.toString(reserve));
|
||||||
|
mOomLevelsSet = true;
|
||||||
}
|
}
|
||||||
// GB: 2048,3072,4096,6144,7168,8192
|
// GB: 2048,3072,4096,6144,7168,8192
|
||||||
// HC: 8192,10240,12288,14336,16384,20480
|
// HC: 8192,10240,12288,14336,16384,20480
|
||||||
@@ -1223,93 +1283,50 @@ public final class ProcessList {
|
|||||||
buf.putInt(LMK_GETKILLCNT);
|
buf.putInt(LMK_GETKILLCNT);
|
||||||
buf.putInt(min_oom_adj);
|
buf.putInt(min_oom_adj);
|
||||||
buf.putInt(max_oom_adj);
|
buf.putInt(max_oom_adj);
|
||||||
if (writeLmkd(buf, repl)) {
|
// indicate what we are waiting for
|
||||||
int i = repl.getInt();
|
repl.putInt(LMK_GETKILLCNT);
|
||||||
if (i != LMK_GETKILLCNT) {
|
repl.rewind();
|
||||||
Slog.e("ActivityManager", "Failed to get kill count, code mismatch");
|
if (writeLmkd(buf, repl) && repl.getInt() == LMK_GETKILLCNT) {
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return new Integer(repl.getInt());
|
return new Integer(repl.getInt());
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GuardedBy("sLmkdSocketLock")
|
boolean onLmkdConnect(OutputStream ostream) {
|
||||||
private static boolean openLmkdSocketLS() {
|
|
||||||
try {
|
try {
|
||||||
sLmkdSocket = new LocalSocket(LocalSocket.SOCKET_SEQPACKET);
|
// Purge any previously registered pids
|
||||||
sLmkdSocket.connect(
|
ByteBuffer buf = ByteBuffer.allocate(4);
|
||||||
new LocalSocketAddress("lmkd",
|
buf.putInt(LMK_PROCPURGE);
|
||||||
LocalSocketAddress.Namespace.RESERVED));
|
ostream.write(buf.array(), 0, buf.position());
|
||||||
sLmkdOutputStream = sLmkdSocket.getOutputStream();
|
if (mOomLevelsSet) {
|
||||||
sLmkdInputStream = sLmkdSocket.getInputStream();
|
// Reset oom_adj levels
|
||||||
} catch (IOException ex) {
|
buf = ByteBuffer.allocate(4 * (2 * mOomAdj.length + 1));
|
||||||
Slog.w(TAG, "lowmemorykiller daemon socket open failed");
|
buf.putInt(LMK_TARGET);
|
||||||
sLmkdSocket = null;
|
for (int i = 0; i < mOomAdj.length; i++) {
|
||||||
return false;
|
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) {
|
} catch (IOException ex) {
|
||||||
Slog.w(TAG, "Error reading from lowmemorykiller socket");
|
return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
IoUtils.closeQuietly(sLmkdSocket);
|
|
||||||
sLmkdSocket = null;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean writeLmkd(ByteBuffer buf, ByteBuffer repl) {
|
private static boolean writeLmkd(ByteBuffer buf, ByteBuffer repl) {
|
||||||
synchronized (sLmkdSocketLock) {
|
if (!sLmkdConnection.isConnected()) {
|
||||||
for (int i = 0; i < 3; i++) {
|
// try to connect immediately and then keep retrying
|
||||||
if (sLmkdSocket == null) {
|
sKillHandler.sendMessage(
|
||||||
if (openLmkdSocketLS() == false) {
|
sKillHandler.obtainMessage(KillHandler.LMKD_RECONNECT_MSG));
|
||||||
try {
|
|
||||||
Thread.sleep(1000);
|
|
||||||
} catch (InterruptedException ie) {
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Purge any previously registered pids
|
// wait for connection retrying 3 times (up to 3 seconds)
|
||||||
ByteBuffer purge_buf = ByteBuffer.allocate(4);
|
if (!sLmkdConnection.waitForConnection(3 * LMKD_RECONNECT_DELAY_MS)) {
|
||||||
purge_buf.putInt(LMK_PROCPURGE);
|
return false;
|
||||||
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) {
|
static void killProcessGroup(int uid, int pid) {
|
||||||
@@ -3214,4 +3231,28 @@ public final class ProcessList {
|
|||||||
mService.doStopUidLocked(uidRec.uid, uidRec);
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user