1 # Copyright (C) 2010 The Android Open Source Project 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # See the License for the specific language governing permissions and 13 # limitations under the License. 14 # 15 # A nawk/gawk script used to extract the list of launchable activities 16 # from an application's manifest (i.e. AndroidManifest.xml). Usage: 17 # 18 # awk -f <this-script> AndroidManifest.xml 19 # 20 21 # 22 # Explanation: 23 # 24 # A given application can have several activities, and each activity 25 # can have several intent filters. We want to only list, in the final 26 # output, the activities which have a intent-filter that contains the 27 # following elements: 28 # 29 # <action android:name="android.intent.action.MAIN" /> 30 # <category android:name="android.intent.category.LAUNCHER" /> 31 # 32 # To do this, we need hooks called when entering and exiting <activity> 33 # and <intent-filter> elements. 34 # 35 36 BEGIN { 37 while ( xml_event() ) { 38 # concat xml event type and tag for simpler comparisons 39 event = XML_TYPE "-" XML_TAG; 40 # When entering a new <activity>, extract its name and set 41 # the 'launchable' flag to false. 42 if ( event == "BEGIN-ACTIVITY" && 43 XML_RPATH == "ACTIVITY/APPLICATION/MANIFEST/" ) { 44 name = XML_ATTR["android:name"]; 45 launchable = 0; 46 } 47 # When exiting an <activity>, check that it has a name and 48 # is launchable. If so, print its name to the output 49 else if ( event == "END-ACTIVITY" && 50 XML_RPATH == "APPLICATION/MANIFEST/" ) { 51 if ( name && launchable ) { 52 # If the name doesn't contain any dot, we consider 53 # that it is just missing the initial one. 54 if (index(name, ".") == 0) { 55 name = "." name 56 } 57 print name; 58 } 59 } 60 # When entering an <intent-filter> inside an <activity>, clear 61 # the 'action' and 'category' variables. They are updated when 62 # we enter the corresponding elements within the intent-filter. 63 else if ( event == "BEGIN-INTENT-FILTER" && 64 XML_RPATH == "INTENT-FILTER/ACTIVITY/APPLICATION/MANIFEST/" ) { 65 action_main = 0; 66 category_launcher = 0; 67 } 68 # When exiting an <intent-filter>, set the 'launchable' flag to true 69 # for the current activity if both 'action' and 'category' have the 70 # correct name. 71 else if ( event == "END-INTENT-FILTER" && 72 XML_RPATH == "ACTIVITY/APPLICATION/MANIFEST/" ) { 73 if ( category_launcher ) { 74 launchable = 1; 75 } 76 } 77 # When entering an <action> element inside an <intent-filter>, record 78 # its name. 79 else if ( event == "BEGIN-ACTION" && 80 XML_RPATH == "ACTION/INTENT-FILTER/ACTIVITY/APPLICATION/MANIFEST/" ) { 81 action_main = 0; 82 if ( XML_ATTR["android:name"] == "android.intent.action.MAIN" ) { 83 action_main = 1; 84 } 85 } 86 # When entering a <category> element inside an <intent-filter>, record 87 # its name. 88 else if ( event == "BEGIN-CATEGORY" && 89 XML_RPATH == "CATEGORY/INTENT-FILTER/ACTIVITY/APPLICATION/MANIFEST/" ) { 90 if ( action_main && XML_ATTR["android:name"] == "android.intent.category.LAUNCHER" ) { 91 category_launcher = 1; 92 } 93 } 94 } 95 } 96 97 98 # 99 # the following is copied directly from xml.awk - see this file for 100 # usage and implementation details. 101 # 102 function xml_event () { 103 RS=">"; 104 XML_TAG=XML_TYPE=""; 105 split("", XML_ATTR); 106 while ( 1 ) { 107 if (_xml_closing) { # delayed direct tag closure 108 XML_TAG = _xml_closing; 109 XML_TYPE = "END"; 110 _xml_closing = ""; 111 _xml_exit(XML_TAG); 112 return 1; 113 } 114 if (getline <= 0) return 0; # read new input line 115 _xml_p = index($0, "<"); # get start marker 116 if (_xml_p == 0) return 0; # end of file (or malformed input) 117 $0 = substr($0, _xml_p) # remove anything before '<' 118 # ignore CData / Comments / Processing instructions / Declarations 119 if (_xml_in_section("<!\\[[Cc][Dd][Aa][Tt][Aa]\\[", "]]") || 120 _xml_in_section("<!--", "--") || 121 _xml_in_section("<\\?", "\\?") || 122 _xml_in_section("<!", "")) { 123 continue; 124 } 125 if (substr($0, 1, 2) == "</") { # is it a closing tag ? 126 XML_TYPE = "END"; 127 $0 = substr($0, 3); 128 } else { # nope, it's an opening one 129 XML_TYPE = "BEGIN"; 130 $0 = substr($0, 2); 131 } 132 XML_TAG = $0 133 sub("[ \r\n\t/].*$", "", XML_TAG); # extract tag name 134 XML_TAG = toupper(XML_TAG); # uppercase it 135 if ( XML_TAG !~ /^[A-Z][-+_.:0-9A-Z]*$/ ) # validate it 136 _xml_panic("Invalid tag name: " XML_TAG); 137 if (XML_TYPE == "BEGIN") { # update reverse path 138 _xml_enter(XML_TAG); 139 } else { 140 _xml_exit(XML_TAG); 141 } 142 sub("[^ \r\n\t]*[ \r\n\t]*", "", $0); # get rid of tag and spaces 143 while ($0) { # process attributes 144 if ($0 == "/") { # deal with direct closing tag, e.g. </foo> 145 _xml_closing = XML_TAG; # record delayed tag closure. 146 break 147 } 148 _xml_attrib = $0; 149 sub(/=.*$/,"",_xml_attrib); # extract attribute name 150 sub(/^[^=]*/,"",$0); # remove it from record 151 _xml_attrib = tolower(_xml_attrib); 152 if ( _xml_attrib !~ /^[a-z][-+_0-9a-z:]*$/ ) # validate it 153 _xml_panic("Invalid attribute name: " _xml_attrib); 154 if (substr($0,1,2) == "=\"") { # value is ="something" 155 _xml_value = substr($0,3); 156 sub(/".*$/,"",_xml_value); 157 sub(/^="[^"]*"/,"",$0); 158 } else if (substr($0,1,2) == "='") { # value is ='something' 159 _xml_value = substr($0,3); 160 sub(/'.*$/,"",_xml_value); 161 sub(/^='[^']*'/,"",$0); 162 } else { 163 _xml_panic("Invalid attribute value syntax for " _xml_attrib ": " $0); 164 } 165 XML_ATTR[_xml_attrib] = _xml_value; # store attribute name/value 166 sub(/^[ \t\r\n]*/,"",$0); # get rid of remaining leading spaces 167 } 168 return 1; # now return, XML_TYPE/TAG/ATTR/RPATH are set 169 } 170 } 171 172 function _xml_panic (msg) { 173 print msg > "/dev/stderr" 174 exit(1) 175 } 176 177 function _xml_in_section (sec_begin, sec_end) { 178 if (!match( $0, "^" sec_begin )) return 0; 179 while (!match($0, sec_end "$")) { 180 if (getline <= 0) _xml_panic("Unexpected EOF: " ERRNO); 181 } 182 return 1; 183 } 184 185 function _xml_enter (tag) { 186 XML_RPATH = tag "/" XML_RPATH; 187 } 188 189 function _xml_exit (tag) { 190 _xml_p = index(XML_RPATH, "/"); 191 _xml_expected = substr(XML_RPATH, 1, _xml_p-1); 192 if (_xml_expected != XML_TAG) 193 _xml_panic("Unexpected close tag: " XML_TAG ", expecting " _xml_expected); 194 XML_RPATH = substr(XML_RPATH, _xml_p+1); 195 } 196