org.chromium.latency.walt.TapLatencyFragment.java Source code

Java tutorial

Introduction

Here is the source code for org.chromium.latency.walt.TapLatencyFragment.java

Source

/*
 * Copyright (C) 2015 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 org.chromium.latency.walt;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.text.method.ScrollingMovementMethod;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.TextView;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Locale;

import static org.chromium.latency.walt.Utils.getBooleanPreference;

public class TapLatencyFragment extends Fragment implements View.OnClickListener {

    private static final int ACTION_DOWN_INDEX = 0;
    private static final int ACTION_UP_INDEX = 1;
    private SimpleLogger logger;
    private TraceLogger traceLogger;
    private WaltDevice waltDevice;
    private TextView logTextView;
    private TextView tapCatcherView;
    private TextView tapCountsView;
    private TextView moveCountsView;
    private ImageButton finishButton;
    private ImageButton restartButton;
    private HistogramChart latencyChart;
    private int moveCount = 0;
    private int allDownCount = 0;
    private int allUpCount = 0;
    private int okDownCount = 0;
    private int okUpCount = 0;
    private boolean shouldShowLatencyChart = false;

    ArrayList<UsMotionEvent> eventList = new ArrayList<>();
    ArrayList<Double> p2kDown = new ArrayList<>();
    ArrayList<Double> p2kUp = new ArrayList<>();
    ArrayList<Double> k2cDown = new ArrayList<>();
    ArrayList<Double> k2cUp = new ArrayList<>();

    private BroadcastReceiver logReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String msg = intent.getStringExtra("message");
            TapLatencyFragment.this.appendLogText(msg);
        }
    };

    private View.OnTouchListener touchListener = new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            UsMotionEvent tapEvent = new UsMotionEvent(event, waltDevice.clock.baseTime);

            if (tapEvent.action != MotionEvent.ACTION_UP && tapEvent.action != MotionEvent.ACTION_DOWN) {
                moveCount++;
                updateCountsDisplay();
                return true;
            }

            // Debug: logger.log("\n"+ action + " event received: " + tapEvent.toStringLong());
            tapEvent.physicalTime = waltDevice.readLastShockTime();

            tapEvent.isOk = checkTapSanity(tapEvent);
            // Save it in any case so we can do stats on bad events later
            eventList.add(tapEvent);

            final double physicalToKernelTime = (tapEvent.kernelTime - tapEvent.physicalTime) / 1000.;
            final double kernelToCallbackTime = (tapEvent.createTime - tapEvent.kernelTime) / 1000.;
            if (tapEvent.action == MotionEvent.ACTION_DOWN) {
                allDownCount++;
                if (tapEvent.isOk) {
                    okDownCount++;
                    p2kDown.add(physicalToKernelTime);
                    k2cDown.add(kernelToCallbackTime);
                    if (shouldShowLatencyChart)
                        latencyChart.addEntry(ACTION_DOWN_INDEX, physicalToKernelTime);
                    logger.log(String.format(Locale.US, "ACTION_DOWN:\ntouch2kernel: %.1f ms\nkernel2java: %.1f ms",
                            physicalToKernelTime, kernelToCallbackTime));
                }
            } else if (tapEvent.action == MotionEvent.ACTION_UP) {
                allUpCount++;
                if (tapEvent.isOk) {
                    okUpCount++;
                    p2kUp.add(physicalToKernelTime);
                    k2cUp.add(kernelToCallbackTime);
                    if (shouldShowLatencyChart)
                        latencyChart.addEntry(ACTION_UP_INDEX, physicalToKernelTime);
                    logger.log(String.format(Locale.US, "ACTION_UP:\ntouch2kernel: %.1f ms\nkernel2java: %.1f ms",
                            physicalToKernelTime, kernelToCallbackTime));
                }
            }
            traceLogEvent(tapEvent);

            updateCountsDisplay();
            return true;
        }
    };

    private void traceLogEvent(UsMotionEvent tapEvent) {
        if (!tapEvent.isOk)
            return;
        if (traceLogger == null)
            return;
        if (tapEvent.action != MotionEvent.ACTION_DOWN && tapEvent.action != MotionEvent.ACTION_UP)
            return;
        final String title = tapEvent.action == MotionEvent.ACTION_UP ? "Tap-Up" : "Tap-Down";
        traceLogger.log(tapEvent.physicalTime + waltDevice.clock.baseTime,
                tapEvent.kernelTime + waltDevice.clock.baseTime, title + " Physical",
                "Bar starts at accelerometer shock and ends at kernel time of tap event");
        traceLogger.log(tapEvent.kernelTime + waltDevice.clock.baseTime,
                tapEvent.createTime + waltDevice.clock.baseTime, title + " App Callback",
                "Bar starts at kernel time of tap event and ends at app callback time");
    }

    public TapLatencyFragment() {
        // Required empty public constructor
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        shouldShowLatencyChart = getBooleanPreference(getContext(), R.string.preference_show_tap_histogram, true);
        if (getBooleanPreference(getContext(), R.string.preference_systrace, true)) {
            traceLogger = TraceLogger.getInstance();
        }
        waltDevice = WaltDevice.getInstance(getContext());
        logger = SimpleLogger.getInstance(getContext());
        // Inflate the layout for this fragment
        final View view = inflater.inflate(R.layout.fragment_tap_latency, container, false);
        restartButton = (ImageButton) view.findViewById(R.id.button_restart_tap);
        finishButton = (ImageButton) view.findViewById(R.id.button_finish_tap);
        tapCatcherView = (TextView) view.findViewById(R.id.tap_catcher);
        logTextView = (TextView) view.findViewById(R.id.txt_log_tap_latency);
        tapCountsView = (TextView) view.findViewById(R.id.txt_tap_counts);
        moveCountsView = (TextView) view.findViewById(R.id.txt_move_count);
        latencyChart = (HistogramChart) view.findViewById(R.id.latency_chart);
        logTextView.setMovementMethod(new ScrollingMovementMethod());
        finishButton.setEnabled(false);
        return view;
    }

    @Override
    public void onResume() {
        super.onResume();

        logTextView.setText(logger.getLogText());
        logger.registerReceiver(logReceiver);

        // Register this fragment class as the listener for some button clicks
        restartButton.setOnClickListener(this);
        finishButton.setOnClickListener(this);
    }

    @Override
    public void onPause() {
        logger.unregisterReceiver(logReceiver);
        super.onPause();
    }

    public void appendLogText(String msg) {
        logTextView.append(msg + "\n");
    }

    public boolean checkTapSanity(UsMotionEvent e) {
        String action = e.getActionString();
        double dt = (e.kernelTime - e.physicalTime) / 1000.0;

        if (e.physicalTime == 0) {
            logger.log(action + " no shock found");
            return false;
        }

        if (dt < 0 || dt > 200) {
            logger.log(action + " bogus kernelTime, ignored, dt=" + dt);
            return false;
        }
        return true;
    }

    void updateCountsDisplay() {
        String tpl = "N %d (%d)  %d (%d)";
        tapCountsView.setText(String.format(Locale.US, tpl, okDownCount, allDownCount, okUpCount, allUpCount));

        moveCountsView.setText(String.format(Locale.US, " %d", moveCount));
    }

    void restartMeasurement() {
        logger.log("\n## Restarting tap latency measurement. Re-sync clocks ...");
        try {
            waltDevice.softReset();
            waltDevice.syncClock();
        } catch (IOException e) {
            logger.log("Error syncing clocks: " + e.getMessage());
            restartButton.setImageResource(R.drawable.ic_play_arrow_black_24dp);
            finishButton.setEnabled(false);
            latencyChart.setVisibility(View.GONE);
            return;
        }

        eventList.clear();
        p2kDown.clear();
        p2kUp.clear();
        k2cDown.clear();
        k2cUp.clear();

        moveCount = 0;
        allDownCount = 0;
        allUpCount = 0;
        okDownCount = 0;
        okUpCount = 0;

        updateCountsDisplay();
        tapCatcherView.setOnTouchListener(touchListener);
    }

    void finishAndShowStats() {
        tapCatcherView.setOnTouchListener(null);
        waltDevice.checkDrift();
        logger.log("\n-------------------------------");
        logger.log(String.format(Locale.US,
                "Tap latency results:\n" + "Number of events recorded:\n" + "   ACTION_DOWN %d (bad %d)\n"
                        + "   ACTION_UP %d (bad %d)\n" + "   ACTION_MOVE %d",
                okDownCount, allDownCount - okDownCount, okUpCount, allUpCount - okUpCount, moveCount));

        logger.log("ACTION_DOWN median times:");
        logger.log(String.format(Locale.US, "   Touch to kernel: %.1f ms\n   Kernel to Java: %.1f ms",
                Utils.median(p2kDown), Utils.median(k2cDown)));
        logger.log("ACTION_UP median times:");
        logger.log(String.format(Locale.US, "   Touch to kernel: %.1f ms\n   Kernel to Java: %.1f ms",
                Utils.median(p2kUp), Utils.median(k2cUp)));
        logger.log("-------------------------------");
        if (traceLogger != null)
            traceLogger.flush(getContext());

        if (shouldShowLatencyChart) {
            latencyChart.setLabel(ACTION_DOWN_INDEX,
                    String.format(Locale.US, "ACTION_DOWN median=%.1f ms", Utils.median(p2kDown)));
            latencyChart.setLabel(ACTION_UP_INDEX,
                    String.format(Locale.US, "ACTION_UP median=%.1f ms", Utils.median(p2kUp)));
        }
        LogUploader.uploadIfAutoEnabled(getContext());
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.button_restart_tap) {
            restartButton.setImageResource(R.drawable.ic_refresh_black_24dp);
            finishButton.setEnabled(true);
            if (shouldShowLatencyChart) {
                latencyChart.setVisibility(View.VISIBLE);
                latencyChart.clearData();
                latencyChart.setLabel(ACTION_DOWN_INDEX, "ACTION_DOWN");
                latencyChart.setLabel(ACTION_UP_INDEX, "ACTION_UP");
            }
            restartMeasurement();
            return;
        }

        if (v.getId() == R.id.button_finish_tap) {
            finishButton.setEnabled(false);
            finishAndShowStats();
            restartButton.setImageResource(R.drawable.ic_play_arrow_black_24dp);
            return;
        }

    }
}