All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.tinylog.runtime.AndroidRuntime Maven / Gradle / Ivy

There is a newer version: 2.8.0-M1
Show newest version
/*
 * Copyright 2016 Martin Winandy
 *
 * 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 org.tinylog.runtime;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Locale;

import android.os.Process;

import dalvik.system.VMStack;

import org.tinylog.Level;
import org.tinylog.provider.InternalLogger;

/**
 * Runtime dialect implementation for Android's Virtual Machine.
 */
final class AndroidRuntime implements RuntimeDialect {

	private static final int STACK_TRACE_SIZE = 10;

	private static final Timestamp startTime = new LegacyTimestamp();

	private final Method stackTraceElementsFiller;
	private final int stackTraceOffset;

	/** */
	AndroidRuntime() {
		StackTraceElementsFiller filler = getStackTraceElementsFiller();
		stackTraceElementsFiller = filler.method;
		stackTraceOffset = filler.index;
	}

	@Override
	public boolean isAndroid() {
		return true;
	}

	@Override
	public String getDefaultWriter() {
		return "logcat";
	}

	@Override
	public long getProcessId() {
		return Process.myPid();
	}

	@Override
	public Timestamp getStartTime() {
		return startTime;
	}

	@Override
	public String getCallerClassName(final int depth) {
		return getCallerStackTraceElement(depth + 1).getClassName();
	}

	@Override
	public String getCallerClassName(final String loggerClassName) {
		StackTraceElement element = getCallerStackTraceElement(loggerClassName);
		return element == null ? null : element.getClassName();
	}

	@Override
	public StackTraceElement getCallerStackTraceElement(final int depth) {
		StackTraceElement[] trace = extractCallerStackTraceElements(depth + stackTraceOffset + 1);
		return trace == null ? new Throwable().getStackTrace()[depth] : trace[trace.length - 1];
	}

	@Override
	public StackTraceElement getCallerStackTraceElement(final String loggerClassName) {
		StackTraceElement[] trace = extractCallerStackTraceElements(stackTraceOffset + STACK_TRACE_SIZE);
		if (trace != null) {
			StackTraceElement element = findStackTraceElement(loggerClassName, trace);
			if (element != null) {
				return element;
			}
		}

		return findStackTraceElement(loggerClassName, new Throwable().getStackTrace());
	}

	@Override
	public Timestamp createTimestamp() {
		return new LegacyTimestamp();
	}

	@Override
	public TimestampFormatter createTimestampFormatter(final String pattern, final Locale locale) {
		return new LegacyTimestampFormatter(pattern, locale);
	}

	/**
	 * Gets {@link VMStack#fillStackTraceElements(Thread, StackTraceElement[])} as accessible method including the
	 * offset position of direct caller.
	 *
	 * @return Method and position if available, {@code null} if unavailable
	 */
	private static StackTraceElementsFiller getStackTraceElementsFiller() {
		try {
			Method method = VMStack.class.getDeclaredMethod("fillStackTraceElements", Thread.class, StackTraceElement[].class);
			method.setAccessible(true);
			StackTraceElement[] trace = new StackTraceElement[STACK_TRACE_SIZE];
			method.invoke(null, Thread.currentThread(), trace);
			for (int i = 0; i < STACK_TRACE_SIZE; ++i) {
				StackTraceElement element = trace[i];
				if (element != null && AndroidRuntime.class.getName().equals(element.getClassName())
					&& "getStackTraceElementsFiller".equals(element.getMethodName())) {
					return new StackTraceElementsFiller(method, i);
				}
			}
			return new StackTraceElementsFiller(null, -1);
		} catch (NoClassDefFoundError error) {
			return new StackTraceElementsFiller(null, -1);
		} catch (Exception ex) {
			return new StackTraceElementsFiller(null, -1);
		}
	}

	/**
	 * Gets the stack trace element that appears before the passed logger class name.
	 * 
	 * @param loggerClassName
	 *            Logger class name that should appear before the expected stack trace element
	 * @param trace
	 *            Source stack trace
	 * @return Found stack trace element or {@code null}
	 */
	private static StackTraceElement findStackTraceElement(final String loggerClassName, final StackTraceElement[] trace) {
		int index = 0;
		
		while (index < trace.length && !loggerClassName.equals(trace[index].getClassName())) {
			index = index + 1;
		}
		
		while (index < trace.length && loggerClassName.equals(trace[index].getClassName())) {
			index = index + 1;
		}
		
		if (index < trace.length) {
			return trace[index];
		} else {
			return null;
		}
	}

	/**
	 * Extracts a defined number of elements from stack trace.
	 * 
	 * @param count
	 *            Number of stack trace elements to extract
	 * @return Extracted stack trace elements
	 */
	private StackTraceElement[] extractCallerStackTraceElements(final int count) {
		if (stackTraceElementsFiller != null) {
			try {
				StackTraceElement[] trace = new StackTraceElement[count + 1];
				stackTraceElementsFiller.invoke(null, Thread.currentThread(), trace);
				return trace;
			} catch (IllegalAccessException ex) {
				InternalLogger.log(Level.ERROR, ex, "Failed getting stack trace element from dalvik.system.VMStack");
			} catch (InvocationTargetException ex) {
				InternalLogger.log(Level.ERROR, ex.getTargetException(), "Failed getting stack trace element from dalvik.system.VMStack");
			}
		}

		return null;
	}

	/**
	 * Data class for storing a method and an index.
	 */
	private static final class StackTraceElementsFiller {

		private final Method method;
		private final int index;

		/**
		 * @param method
		 *            {@link VMStack#fillStackTraceElements(Thread, StackTraceElement[])}
		 * @param index
		 *            Offset position of caller
		 */
		private StackTraceElementsFiller(final Method method, final int index) {
			this.method = method;
			this.index = index;
		}

	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy