Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 57240b0a authored by Li Li's avatar Li Li Committed by Automerger Merge Worker
Browse files

Merge "Freezer: fix exception when parsing /proc/locks" into sc-dev am: ef12967b

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15395534

Change-Id: I7dd879701d75eda12ef546e5c1da751a82c4c5a6
parents d84bc01f ef12967b
Loading
Loading
Loading
Loading
+0 −42
Original line number Diff line number Diff line
@@ -34,12 +34,9 @@ import dalvik.system.VMRuntime;

import libcore.io.IoUtils;

import java.io.BufferedReader;
import java.io.FileDescriptor;
import java.io.FileReader;
import java.io.IOException;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.concurrent.TimeoutException;

/**
@@ -1472,43 +1469,4 @@ public class Process {
    }

    private static native int nativePidFdOpen(int pid, int flags) throws ErrnoException;

    /**
     * Checks if a process corresponding to a specific pid owns any file locks.
     * @param pid The process ID for which we want to know the existence of file locks.
     * @return true If the process holds any file locks, false otherwise.
     * @throws IOException if /proc/locks can't be accessed.
     *
     * @hide
     */
    public static boolean hasFileLocks(int pid) throws Exception {
        BufferedReader br = null;

        try {
            br = new BufferedReader(new FileReader("/proc/locks"));
            String line;

            while ((line = br.readLine()) != null) {
                StringTokenizer st = new StringTokenizer(line);

                for (int i = 0; i < 5 && st.hasMoreTokens(); i++) {
                    String str = st.nextToken();
                    try {
                        if (i == 4 && Integer.parseInt(str) == pid) {
                            return true;
                        }
                    } catch (NumberFormatException nfe) {
                        throw new Exception("Exception parsing /proc/locks at \" "
                                + line +  " \", token #" + i);
                    }
                }
            }

            return false;
        } finally {
            if (br != null) {
                br.close();
            }
        }
    }
}
+89 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.internal.os;

import com.android.internal.util.ProcFileReader;

import libcore.io.IoUtils;

import java.io.FileInputStream;
import java.io.IOException;

/**
 * Reads and parses {@code locks} files in the {@code proc} filesystem.
 * A typical example of /proc/locks
 *
 * 1: POSIX  ADVISORY  READ  18403 fd:09:9070 1073741826 1073742335
 * 2: POSIX  ADVISORY  WRITE 18292 fd:09:34062 0 EOF
 * 2: -> POSIX  ADVISORY  WRITE 18291 fd:09:34062 0 EOF
 * 2: -> POSIX  ADVISORY  WRITE 18293 fd:09:34062 0 EOF
 * 3: POSIX  ADVISORY  READ  3888 fd:09:13992 128 128
 * 4: POSIX  ADVISORY  READ  3888 fd:09:14230 1073741826 1073742335
 */
public class ProcLocksReader {
    private final String mPath;

    public ProcLocksReader() {
        mPath = "/proc/locks";
    }

    public ProcLocksReader(String path) {
        mPath = path;
    }

    /**
     * Checks if a process corresponding to a specific pid owns any file locks.
     * @param pid The process ID for which we want to know the existence of file locks.
     * @return true If the process holds any file locks, false otherwise.
     * @throws IOException if /proc/locks can't be accessed.
     */
    public boolean hasFileLocks(int pid) throws Exception {
        ProcFileReader reader = null;
        long last = -1;
        long id; // ordinal position of the lock in the list
        int owner; // the PID of the process that owns the lock

        try {
            reader = new ProcFileReader(new FileInputStream(mPath));

            while (reader.hasMoreData()) {
                id = reader.nextLong(true); // lock id
                if (id == last) {
                    reader.finishLine(); // blocked lock
                    continue;
                }

                reader.nextIgnored(); // lock type: POSIX?
                reader.nextIgnored(); // lock type: MANDATORY?
                reader.nextIgnored(); // lock type: RW?

                owner = reader.nextInt(); // pid
                if (owner == pid) {
                    return true;
                }
                reader.finishLine();
                last = id;
            }
        } catch (IOException e) {
            // TODO: let ProcFileReader log the failed line
            throw new Exception("Exception parsing /proc/locks");
        } finally {
            IoUtils.closeQuietly(reader);
        }
        return false;
    }
}
+37 −6
Original line number Diff line number Diff line
@@ -28,8 +28,8 @@ import java.nio.charset.StandardCharsets;
 * requires each line boundary to be explicitly acknowledged using
 * {@link #finishLine()}. Assumes {@link StandardCharsets#US_ASCII} encoding.
 * <p>
 * Currently doesn't support formats based on {@code \0}, tabs, or repeated
 * delimiters.
 * Currently doesn't support formats based on {@code \0}, tabs.
 * Consecutive spaces are treated as a single delimiter.
 */
public class ProcFileReader implements Closeable {
    private final InputStream mStream;
@@ -75,6 +75,11 @@ public class ProcFileReader implements Closeable {
    private void consumeBuf(int count) throws IOException {
        // TODO: consider moving to read pointer, but for now traceview says
        // these copies aren't a bottleneck.

        // skip all consecutive delimiters.
        while (count < mTail && mBuffer[count] == ' ') {
            count++;
        }
        System.arraycopy(mBuffer, count, mBuffer, 0, mTail - count);
        mTail -= count;
        if (mTail == 0) {
@@ -159,11 +164,18 @@ public class ProcFileReader implements Closeable {
     * Parse and return next token as base-10 encoded {@code long}.
     */
    public long nextLong() throws IOException {
        return nextLong(false);
    }

    /**
     * Parse and return next token as base-10 encoded {@code long}.
     */
    public long nextLong(boolean stopAtInvalid) throws IOException {
        final int tokenIndex = nextTokenIndex();
        if (tokenIndex == -1) {
            throw new ProtocolException("Missing required long");
        } else {
            return parseAndConsumeLong(tokenIndex);
            return parseAndConsumeLong(tokenIndex, stopAtInvalid);
        }
    }

@@ -176,7 +188,7 @@ public class ProcFileReader implements Closeable {
        if (tokenIndex == -1) {
            return def;
        } else {
            return parseAndConsumeLong(tokenIndex);
            return parseAndConsumeLong(tokenIndex, false);
        }
    }

@@ -186,7 +198,10 @@ public class ProcFileReader implements Closeable {
        return s;
    }

    private long parseAndConsumeLong(int tokenIndex) throws IOException {
    /**
     * If stopAtInvalid is true, don't throw IOException but return whatever parsed so far.
     */
    private long parseAndConsumeLong(int tokenIndex, boolean stopAtInvalid) throws IOException {
        final boolean negative = mBuffer[0] == '-';

        // TODO: refactor into something like IntegralToString
@@ -194,8 +209,12 @@ public class ProcFileReader implements Closeable {
        for (int i = negative ? 1 : 0; i < tokenIndex; i++) {
            final int digit = mBuffer[i] - '0';
            if (digit < 0 || digit > 9) {
                if (stopAtInvalid) {
                    break;
                } else {
                    throw invalidLong(tokenIndex);
                }
            }

            // always parse as negative number and apply sign later; this
            // correctly handles MIN_VALUE which is "larger" than MAX_VALUE.
@@ -226,6 +245,18 @@ public class ProcFileReader implements Closeable {
        return (int) value;
    }

    /**
     * Bypass the next token.
     */
    public void nextIgnored() throws IOException {
        final int tokenIndex = nextTokenIndex();
        if (tokenIndex == -1) {
            throw new ProtocolException("Missing required token");
        } else {
            consumeBuf(tokenIndex + 1);
        }
    }

    @Override
    public void close() throws IOException {
        mStream.close();
+90 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.internal.os;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import android.content.Context;
import android.os.FileUtils;

import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.io.File;
import java.nio.file.Files;

@SmallTest
@RunWith(AndroidJUnit4.class)
public class ProcLocksReaderTest {
    private File mProcDirectory;

    @Before
    public void setUp() {
        Context context = InstrumentationRegistry.getContext();
        mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE);
    }

    @After
    public void tearDown() throws Exception {
        FileUtils.deleteContents(mProcDirectory);
    }

    @Test
    public void testRunSimpleLocks() throws Exception {
        String simpleLocks =
                "1: POSIX  ADVISORY  READ  18403 fd:09:9070 1073741826 1073742335\n" +
                "2: POSIX  ADVISORY  WRITE 18292 fd:09:34062 0 EOF\n";
        assertFalse(runHasFileLocks(simpleLocks, 18402));
        assertFalse(runHasFileLocks(simpleLocks, 18404));
        assertTrue(runHasFileLocks(simpleLocks, 18403));
        assertTrue(runHasFileLocks(simpleLocks, 18292));
    }

    @Test
    public void testRunBlockedLocks() throws Exception {
        String blockedLocks =
                "1: POSIX  ADVISORY  READ  18403 fd:09:9070 1073741826 1073742335\n" +
                "2: POSIX  ADVISORY  WRITE 18292 fd:09:34062 0 EOF\n" +
                "2: -> POSIX  ADVISORY  WRITE 18291 fd:09:34062 0 EOF\n" +
                "2: -> POSIX  ADVISORY  WRITE 18293 fd:09:34062 0 EOF\n" +
                "3: POSIX  ADVISORY  READ  3888 fd:09:13992 128 128\n" +
                "4: POSIX  ADVISORY  READ  3888 fd:09:14230 1073741826 1073742335\n";
        assertFalse(runHasFileLocks(blockedLocks, 18402));
        assertFalse(runHasFileLocks(blockedLocks, 18404));
        assertTrue(runHasFileLocks(blockedLocks, 18403));
        assertTrue(runHasFileLocks(blockedLocks, 18292));

        assertFalse(runHasFileLocks(blockedLocks, 18291));
        assertFalse(runHasFileLocks(blockedLocks, 18293));
        assertTrue(runHasFileLocks(blockedLocks, 3888));
    }

    private boolean runHasFileLocks(String fileContents, int pid) throws Exception {
        File tempFile = File.createTempFile("locks", null, mProcDirectory);
        Files.write(tempFile.toPath(), fileContents.getBytes());
        boolean result = new ProcLocksReader(tempFile.toString()).hasFileLocks(pid);
        Files.delete(tempFile.toPath());
        return result;
    }
}
+40 −0
Original line number Diff line number Diff line
@@ -166,6 +166,46 @@ public class ProcFileReaderTest extends AndroidTestCase {
        assertEquals(-1L, reader.nextOptionalLong(-1L));
    }

    public void testInvalidLongs() throws Exception {
        final ProcFileReader reader = buildReader("12: 34\n56 78@#\n");

        assertEquals(12L, reader.nextLong(true));
        assertEquals(34L, reader.nextLong(true));
        reader.finishLine();
        assertTrue(reader.hasMoreData());

        assertEquals(56L, reader.nextLong(true));
        assertEquals(78L, reader.nextLong(true));
        reader.finishLine();
        assertFalse(reader.hasMoreData());
    }

    public void testConsecutiveDelimiters() throws Exception {
        final ProcFileReader reader = buildReader("1 2  3   4     5\n");

        assertEquals(1L, reader.nextLong());
        assertEquals(2L, reader.nextLong());
        assertEquals(3L, reader.nextLong());
        assertEquals(4L, reader.nextLong());
        assertEquals(5L, reader.nextLong());
        reader.finishLine();
        assertFalse(reader.hasMoreData());
    }

    public void testIgnore() throws Exception {
        final ProcFileReader reader = buildReader("a b c\n");

        assertEquals("a", reader.nextString());
        assertTrue(reader.hasMoreData());

        reader.nextIgnored();
        assertTrue(reader.hasMoreData());

        assertEquals("c", reader.nextString());
        reader.finishLine();
        assertFalse(reader.hasMoreData());
    }

    private static ProcFileReader buildReader(String string) throws IOException {
        return buildReader(string, 2048);
    }
Loading