hihex.cs.Config.java Source code

Java tutorial

Introduction

Here is the source code for hihex.cs.Config.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.content.Context;

import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Throwables;
import com.google.common.util.concurrent.UncheckedExecutionException;

import java.io.IOException;
import java.io.Writer;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;

/**
 * Shared configuration between the WebServer and LogCollector.
 */
public final class Config {
    public static final int[] EMPTY_PID_ARRAY = {};
    public static final int[] EMPTY_PID_ARRAY_LIVE_ALLOWED = {};

    public final Context context;

    public final Preferences preferences;
    public final LogFiles logFiles;
    public final ChunkRenderer renderer;

    public final PidDatabase pidDatabase = new PidDatabase();

    private final AtomicInteger mSystemServerPid = new AtomicInteger(-1);
    private final AtomicInteger mDebuggerdPid = new AtomicInteger(-1);

    public Config(final Context context) {
        this.context = context;
        preferences = new Preferences(context);
        logFiles = new LogFiles(context);
        renderer = new ChunkRenderer(context);
    }

    public void refreshPids() {
        mSystemServerPid.set(-1);
        mDebuggerdPid.set(-1);
        pidDatabase.refresh();
    }

    private int findPidForExactProcessName(final AtomicInteger cache, final String processName) {
        final int pid = cache.get();
        if (pid != -1) {
            return pid;
        }

        final int newPid = pidDatabase.findPidForExactProcessName(processName);
        if (!cache.compareAndSet(-1, newPid)) {
            return cache.get();
        } else {
            return newPid;
        }
    }

    public int systemServerPid() {
        return findPidForExactProcessName(mSystemServerPid, "system_server");
    }

    public int debuggerdPid() {
        return findPidForExactProcessName(mDebuggerdPid, "/system/bin/debuggerd");
    }

    public Optional<Writer> startRecording(final int pid, final Optional<String> processName, final Date timestamp)
            throws IOException {
        final Writer[] optWriter = { null };
        try {
            pidDatabase.startRecording(pid, processName, logFiles, timestamp, new Function<PidEntry, Void>() {
                @Override
                public Void apply(final PidEntry entry) {
                    final Writer writer = entry.writer.get();
                    optWriter[0] = writer;
                    try {
                        renderer.writeHeader(writer, entry.pid, processName.or(entry.processName), timestamp);
                    } catch (final IOException e) {
                        throw new UncheckedExecutionException(e);
                    }
                    return null;
                }
            });
        } catch (final UncheckedExecutionException e) {
            final Throwable cause = e.getCause();
            Throwables.propagateIfInstanceOf(cause, IOException.class);
            throw Throwables.propagate(cause);
        }
        Events.bus.post(new Events.RecordCount(pidDatabase.countRecordingEntries()));
        return Optional.fromNullable(optWriter[0]);
    }

    public void stopRecording(final int pid, final Function<Writer, ?> cleanup) {
        pidDatabase.stopRecording(pid, new Function<Writer, Void>() {
            @Override
            public Void apply(final Writer writer) {
                try {
                    cleanup.apply(writer);
                    renderer.writeFooter(writer);
                } catch (final IOException e) {
                    // Ignore.
                }
                return null;
            }
        });
        Events.bus.post(new Events.RecordCount(pidDatabase.countRecordingEntries()));
    }

    public Optional<Writer> splitLogAndGetWriter(final int pid) {
        final Optional<PidEntry> optEntry = pidDatabase.getEntry(pid);
        if (optEntry.isPresent()) {
            final PidEntry entry = optEntry.get();
            final long splitSize = preferences.getSplitSize();
            if (splitSize >= 0 && entry.path.isPresent() && entry.path.get().length() >= splitSize) {
                try {
                    renderer.writeFooter(entry.writer.get());
                } catch (final IOException e) {
                    // Ignore.
                }
                final PidEntry splitEntry = pidDatabase.splitEntry(pid, logFiles);
                try {
                    renderer.writeHeader(splitEntry.writer.get(), splitEntry.pid, splitEntry.processName,
                            new Date());
                } catch (final IOException e) {
                    // Ignore.
                }
                return splitEntry.writer;
            } else {
                return entry.writer;
            }
        } else {
            return Optional.absent();
        }
    }

    public void flushWriter(final String filename) {
        final Optional<PidEntry> entry = pidDatabase.findEntry(filename);
        if (!entry.isPresent()) {
            return;
        }
        final Optional<Writer> writer = entry.get().writer;
        if (!entry.isPresent()) {
            return;
        }
        try {
            writer.get().flush();
        } catch (final IOException e) {
            // Flush failure, but ignore.
        }
    }

    public final void startRecordingExistingProcesses() {
        final Date now = new Date();
        final Pattern filter = preferences.getFilter();
        for (final HashMap<String, String> entry : pidDatabase.runningProcesses()) {
            final String name = entry.get("name");
            if (filter.matcher(name).find() && !entry.containsKey("recording")) {
                final int pid = Integer.parseInt(entry.get("pid"));
                try {
                    startRecording(pid, Optional.of(name), now);
                } catch (final IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * Obtains the array of concrete process IDs that should record this log entry.
     *
     * @param entry The entry to be recorded.
     * @return The array of process IDs to record this entry. If this array is empty, there are two possible return
     * values:
     * <ul>
     * <li>{@link #EMPTY_PID_ARRAY}  no processes should record this entry at all</li>
     * <li>{@link #EMPTY_PID_ARRAY_LIVE_ALLOWED}  no processes need to record this entry, but this entry may be used in
     * live logging.</li>
     * </ul>
     */
    public int[] getFilteredPidsForLog(final LogEntry entry) {
        entry.populateProcessName(pidDatabase);

        final String source = entry.getProcessName();
        final Set<String> targets = pidDatabase.listRecordingProcessNames();
        final HashSet<String> filtered = preferences.getLogFilter().filter(entry, source, targets);
        if (filtered.isEmpty()) {
            return EMPTY_PID_ARRAY;
        }
        targets.remove(LogEntryFilter.LIVE_SOURCE);
        if (filtered.isEmpty()) {
            return EMPTY_PID_ARRAY_LIVE_ALLOWED;
        }

        final int[] pids = new int[filtered.size()];
        int i = 0;
        for (final String target : filtered) {
            final int targetPid;
            if (target.equals(source)) {
                targetPid = entry.pid();
            } else {
                targetPid = pidDatabase.findPidForExactProcessName(target);
            }
            pids[i] = targetPid;
            ++i;
        }
        return pids;
    }
}