diff options
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.java | 306 |
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; + } + + } +} |