hihex.cs.PidEntry.java Source code

Java tutorial

Introduction

Here is the source code for hihex.cs.PidEntry.java

Source

/*
 * CatSaver
 * Copyright (C) 2015 HiHex Ltd.
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package hihex.cs;

import android.util.SparseArray;

import com.google.common.base.Charsets;
import com.google.common.base.Optional;
import com.google.common.io.Closeables;
import com.google.common.io.Files;

import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Date;
import java.util.Locale;

public final class PidEntry {
    private static final String UNSAFE_FILENAME_PATTERN = "[^-_.,;a-zA-Z0-9]";
    private static final String CONTINUED_SUFFIX = "( \\([^)]+\\))*\\.html\\.gz$";

    public final int pid;
    public final String processName;
    public final Optional<File> path;
    public final Optional<Writer> writer;
    private final SparseArray<String> mThreadNames;

    private PidEntry(final int pid, final String processName, final File path, final Writer writer,
            final SparseArray<String> threadNames) {
        this.pid = pid;
        this.processName = processName;
        this.path = Optional.of(path);
        this.writer = Optional.of(writer);
        mThreadNames = threadNames;
    }

    private PidEntry(final int pid, final String processName, final SparseArray<String> threadNames) {
        this.pid = pid;
        this.processName = processName;
        path = Optional.absent();
        writer = Optional.absent();
        mThreadNames = threadNames;
    }

    public PidEntry(final int pid, final String processName) {
        this(pid, processName, new SparseArray<String>());
    }

    /**
     * Close the file it is currently writing to.
     */
    public PidEntry close() {
        if (writer.isPresent()) {
            final Writer writer2 = writer.get();
            try {
                writer2.flush();
            } catch (final IOException e) {
                CsLog.e("Failed to flush file, some data may not be written. " + e);
            } finally {
                try {
                    Closeables.close(writer2, true);
                } catch (IOException e) {
                    // Ignore.
                }
            }
        }
        return new PidEntry(pid, processName, mThreadNames);
    }

    /**
     * Open a new file, using the timestamp to name the file if possible.
     */
    public PidEntry open(final LogFiles logFiles, final Date timestamp) throws IOException {
        if (writer.isPresent()) {
            return this;
        }

        final String safeName = processName.replaceAll(UNSAFE_FILENAME_PATTERN, "-");
        final String filePrefix = String.format(Locale.ROOT, "%2$s-%1$tF-%1$tH.%1$tM.%1$tS", timestamp, safeName);

        // We try "xxx.html.gz", "xxx (1).html.gz", "xxx (2).html.gz", ... until a filename is free to use.
        final File path = logFiles.getNewPath(filePrefix, ".html.gz");
        final Writer writer = new OutputStreamWriter(new FlushableGzipOutputStream(path), Charsets.UTF_8);

        return new PidEntry(pid, processName, path, writer, mThreadNames);
    }

    public PidEntry split(final LogFiles logFiles) throws IOException {
        if (!writer.isPresent()) {
            return this;
        }

        final String fileName = path.get().getName().replaceAll(CONTINUED_SUFFIX, "");
        final File newPath = logFiles.getNewPath(fileName + " (continued)", ".html.gz");
        final Writer newWriter = new OutputStreamWriter(new FlushableGzipOutputStream(newPath), Charsets.UTF_8);

        close();
        return new PidEntry(pid, processName, newPath, newWriter, mThreadNames);
    }

    public String getThreadName(final int tid) {
        final String cachedThreadName = mThreadNames.get(tid);
        if (cachedThreadName != null) {
            return cachedThreadName;
        }

        final File threadNameFile = new File("/proc/" + pid + "/task/" + tid + "/comm");
        String threadName;
        try {
            threadName = Files.toString(threadNameFile, Charsets.UTF_8);
        } catch (final IOException e) {
            threadName = "TID:" + tid;
        }
        mThreadNames.append(tid, threadName);

        return threadName;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;

        PidEntry pidEntry = (PidEntry) o;

        return pid == pidEntry.pid && processName.equals(pidEntry.processName);
    }

    @Override
    public int hashCode() {
        int result = pid;
        result = 31 * result + processName.hashCode();
        return result;
    }
}