1#!/bin/bash
2# Copyright 2018, The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16if [[ -z $ANDROID_BUILD_TOP ]]; then
17  echo "Please run source build/envsetup.sh first" >&2
18  exit 1
19fi
20
21source $ANDROID_BUILD_TOP/build/envsetup.sh
22
23verbose_print() {
24  if [[ "$verbose" == "y" ]]; then
25    echo "$@" >&2
26  fi
27}
28
29remote_pidof() {
30  local procname="$1"
31  adb shell ps | grep "$procname" | awk '{print $2;}'
32}
33
34remote_pkill() {
35  local procname="$1"
36  shift
37
38  local the_pids=$(remote_pidof "$procname")
39  local pid
40
41  for pid in $the_pids; do
42    verbose_print adb shell kill "$@" "$pid"
43    adb shell kill "$@" "$pid"
44  done
45}
46
47get_activity_name() {
48  local package="$1"
49  local action_key="android.intent.action.MAIN:"
50
51  # Example query-activities output being parsed:
52  #
53  #  Activity #14:
54  #    priority=0 preferredOrder=0 match=0x108000 specificIndex=-1 isDefault=true
55  #    com.google.android.videos/com.google.android.youtube.videos.EntryPoint
56  #  Activity #15:
57  #    priority=0 preferredOrder=0 match=0x108000 specificIndex=-1 isDefault=true
58  #    com.google.android.youtube/.app.honeycomb.Shell$HomeActivity
59
60  # Given package 'com.google.android.youtube' return '.app.honeycomb.Shell$HomeActivity'
61
62  local activity_line="$(adb shell cmd package query-activities --brief -a android.intent.action.MAIN -c android.intent.category.LAUNCHER | grep "$package/")"
63  IFS="/" read -a array <<< "$activity_line"
64  local activity_name="${array[1]}"
65
66  # Activities starting with '.' are shorthand for having their package name prefixed.
67  if [[ $activity_name == .* ]]; then
68    activity_name="${package}${activity_name}"
69  fi
70  echo "$activity_name"
71}
72
73# Use with logcat_from_timestamp to skip all past log-lines.
74logcat_save_timestamp() {
75  adb shell 'date -u +"%Y-%m-%d %H:%M:%S.%N"'
76}
77
78# Roll forward logcat to only show events
79# since the specified timestamp.
80#
81# i.e. don't look at historical logcat,
82# only look at FUTURE logcat.
83#
84# First use 'logcat_save_timestamp'
85# Then do whatever action you want.
86# Then use 'logcat_from_timestamp_bg $timestamp'
87logcat_from_timestamp_bg() {
88  local timestamp="$1"
89  shift # drop timestamp from args.
90  verbose_print adb logcat -T \"$timestamp\" \"$@\"
91  adb logcat -v UTC -T "$timestamp" "$@" &
92  logcat_from_timestamp_pid=$!
93}
94
95# Starting at timestamp $2, wait until we seen pattern $3
96# or until a timeout happens in $1 seconds.
97# If successful, also echo the line that matched the pattern.
98#
99# Set VERBOSE_LOGCAT=1 to debug every line of logcat it tries to parse.
100logcat_select_pattern() {
101  local timeout="$1"
102  local timestamp="$2"
103  local pattern="$3"
104
105  local logcat_fd
106
107  coproc logcat_fd {
108    kill_children_quietly() {
109      kill "$logcat_pidd"
110      wait "$logcat_pidd" 2>/dev/null
111    }
112
113    trap 'kill_children_quietly' EXIT # kill logcat when this coproc is killed.
114
115    # run logcat in the background so it can be killed.
116    logcat_from_timestamp_bg "$timestamp"
117    logcat_pidd=$logcat_from_timestamp_pid
118    wait "$logcat_pidd"
119  }
120  local logcat_pid="$!"
121  verbose_print "[LOGCAT] Spawn pid $logcat_pid"
122
123  local timeout_ts="$(date -d "now + ${timeout} seconds" '+%s')"
124  local now_ts="0"
125
126  local return_code=1
127
128  verbose_print "logcat_wait_for_pattern begin"
129
130  while read -t "$timeout" -r -u "${logcat_fd[0]}" logcat_output; do
131    if (( $VERBOSE_LOGCAT )); then
132      verbose_print "LOGCAT: $logcat_output"
133    fi
134    if [[ "$logcat_output:" == *"$pattern"* ]]; then
135      verbose_print "LOGCAT: " "$logcat_output"
136      verbose_print "WE DID SEE PATTERN" '<<' "$pattern" '>>.'
137      echo "$logcat_output"
138      return_code=0
139      break
140    fi
141    now_ts="$(date -d "now" '+%s')"
142    if (( now_ts >= timeout_ts )); then
143      verbose_print "DID TIMEOUT BEFORE SEEING ANYTHING (timeout=$timeout seconds) " '<<' "$pattern" '>>.'
144      break
145    fi
146  done
147
148  # Don't leave logcat lying around since it will keep going.
149  kill "$logcat_pid"
150  # Suppress annoying 'Terminated...' message.
151  wait "$logcat_pid" 2>/dev/null
152
153  verbose_print "[LOGCAT] $logcat_pid should be killed"
154
155  return $return_code
156}
157
158# Starting at timestamp $2, wait until we seen pattern $3
159# or until a timeout happens in $1 seconds.
160#
161# Set VERBOSE_LOGCAT=1 to debug every line of logcat it tries to parse.
162logcat_wait_for_pattern() {
163  logcat_select_pattern "$@" > /dev/null
164}
165
166# Starting at timestamp $2, wait until we seen pattern $3
167# or until a timeout happens in $1 seconds.
168# If successful, extract with the regular expression pattern in #4
169# and return the first capture group.
170#
171# Set VERBOSE_LOGCAT=1 to debug every line of logcat it tries to parse.
172logcat_extract_pattern() {
173  local timeout="$1"
174  local timestamp="$2"
175  local pattern="$3"
176  local re_pattern="$4"
177
178  local result
179  local exit_code
180
181  result="$(logcat_select_pattern "$@")"
182  exit_code=$?
183
184  if [[ $exit_code -ne 0 ]]; then
185    return $exit_code
186  fi
187
188  echo "$result" | sed 's/'"$re_pattern"'/\1/g'
189}
190
191# Join array
192#   FOO=(a b c)
193#   join_by , "${FOO[@]}" #a,b,c
194join_by() {
195  local IFS="$1"
196  shift
197  echo "$*"
198}
199