def _audio_flinger_handler(self, buffer_size_frames=None): """ Parses audio callback routines. """ all_tasks = self._trace.cpu.task_intervals() self._OSL_cbks = IntervalList(filter_by_task(all_tasks, 'name', 'OSLcbk', 'any')) self._fRdy2s = self._trace.android.event_intervals(name='fRdy2') # Lets estimate buffer size (in seconds) # By taking geomean of intervals between OSL callbacks cbk_delta = [cbk_b.interval.start - cbk_a.interval.start \ for cbk_a, cbk_b in zip(self._OSL_cbks, self._OSL_cbks[1:])] self.buffer_size_seconds = round(percentile(cbk_delta, 0.9), 3) # Find audio jitter intervals # This is delta (in seconds) between expected OSL callback arrival time # and actual arrival time. We want this interval reasonably small. self._cbk_jitters = IntervalList() for cbk_a, cbk_b in zip(self._OSL_cbks, self._OSL_cbks[1:]): expected_cbk_arrival_time = \ cbk_a.interval.start + self.buffer_size_seconds delta = cbk_b.interval.start - expected_cbk_arrival_time interval = Interval(cbk_a.interval.start, cbk_b.interval.start) self._cbk_jitters.append(AudioJitter(interval=interval, latency=delta)) if self.buffer_size_frames is None: # Estimate buffer sizes (frames) self.buffer_size_frames = int(percentile([frdy.value for frdy in self._fRdy2s if frdy.value >= 0], 0.9)) else: self.buffer_size_frames = buffer_size_frames
def _input_intervals(): """ Generator that yields intervals when discrete input event(s) are read & decoded by Android `Input Reader`. x__x__x____IR___ID_ID_ID___DI_SU__DI_SU__DI_SU______ x = multiple input IRQs (multi-touch translated by Android Input Framework) IR = Input Reader [read/decodes multiple events @ once] ID = Input Dispatch [dispatches each input event] DI = Deliver Input [ appropriate window consumes input event ] SU = SurfaceFlinger Screen Update due to window handling input event Please note InputReader 'iq' will be set to 1 whenever InputReader had event to process. This could be disabled in some systems. """ last_timestamp = self._trace.interval.start for ir_event in filter_by_task(all_tasks, 'name', 'InputReader', 'any'): yield Interval(last_timestamp, ir_event.interval.end) last_timestamp = ir_event.interval.end
def _input_latency_handler(self, irq_name): """ Returns list of all input events """ self._input_latencies = IntervalList() all_tasks = self._trace.cpu.task_intervals() all_aq_events = self.input_events() touch_irqs = IntervalList( filter_by_task(all_tasks, 'name', irq_name, 'any')) def _input_intervals(): """ Generator that yields intervals when discrete input event(s) are read & decoded by Android `Input Reader`. x__x__x____IR___ID_ID_ID___DI_SU__DI_SU__DI_SU______ x = multiple input IRQs (multi-touch translated by Android Input Framework) IR = Input Reader [read/decodes multiple events @ once] ID = Input Dispatch [dispatches each input event] DI = Deliver Input [ appropriate window consumes input event ] SU = SurfaceFlinger Screen Update due to window handling input event Please note InputReader 'iq' will be set to 1 whenever InputReader had event to process. This could be disabled in some systems. """ last_timestamp = self._trace.interval.start for ir_event in filter_by_task(all_tasks, 'name', 'InputReader', 'any'): yield Interval(last_timestamp, ir_event.interval.end) last_timestamp = ir_event.interval.end for interval in _input_intervals(): irqs = touch_irqs.slice(interval=interval, trimmed=False) # Necessary as we may be interested in different IRQ name if irqs: # Use longest IRQ start_ts = max( irqs, key=lambda x: x.interval.duration).interval.start end_ts = start_ts post_ir_interval = Interval(start_ts, self._trace.duration) di_events = self.event_intervals( name=['deliverInputEvent', 'input'], interval=post_ir_interval) if di_events: # IMPORTANT: If InputDispatcher sythesizes multiple # events to same application, we ignore consequent event # and only parse 1st event. This is because we heuristically # can't determine start of next input event to differentiate. di_event = di_events[0] # necessary in case a synthetic events is cancelled # canceled appropriately when the events are no longer # being resynthesized (because the application or IME is # already handling them or dropping them entirely) # This is done by checking for dumping input latencies when # active input event queue length (aq) is > 1 for same task. # For more details, see # https://android.googlesource.com/platform/frameworks/base.git/+ # /f9e989d5f09e72f5c9a59d713521f37d3fdd93dd%5E!/ # This returns first interval when aq has pending event(s) di_event_name = getattr(di_event, 'name', None) if di_event_name and di_event_name == 'input': pfb_events = self.event_intervals( name='doComposition', interval=post_ir_interval) else: aq_event = filter_by_task( all_aq_events.slice(interval=post_ir_interval), 'pid', di_event.event.task.pid) if aq_event and aq_event.value > 0: post_di_start = aq_event.interval.start else: if aq_event: continue # if AQ event exists. post_di_start = di_events[0].interval.start post_di_interval = Interval(post_di_start, self._trace.duration) pfb_events = self.event_intervals( name='doComposition', interval=post_di_interval) if pfb_events: end_ts = pfb_events[0].interval.end if start_ts != end_ts and end_ts > start_ts and start_ts not in self._input_latencies._start_timestamps: input_interval = Interval(start=start_ts, end=end_ts) self._input_latencies.append( InputLatency(interval=input_interval, latency=input_interval.duration)) return self._input_latencies