aboutsummaryrefslogtreecommitdiff
path: root/android/WALT/app/src/main/java/org/chromium/latency/walt/TapLatencyFragment.java
diff options
context:
space:
mode:
Diffstat (limited to 'android/WALT/app/src/main/java/org/chromium/latency/walt/TapLatencyFragment.java')
-rw-r--r--android/WALT/app/src/main/java/org/chromium/latency/walt/TapLatencyFragment.java306
1 files changed, 306 insertions, 0 deletions
diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/TapLatencyFragment.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/TapLatencyFragment.java
new file mode 100644
index 0000000..64e333d
--- /dev/null
+++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/TapLatencyFragment.java
@@ -0,0 +1,306 @@
+/*
+ * 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;
+ }
+
+ }
+}