OLD | NEW |
1 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 # Copyright 2015 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """Module containing utilities for apk packages.""" | 5 # pylint: disable=unused-wildcard-import |
| 6 # pylint: disable=wildcard-import |
6 | 7 |
7 import os.path | 8 from devil.android.apk_helper import * |
8 import re | |
9 | |
10 from pylib import cmd_helper | |
11 from pylib import constants | |
12 from pylib.sdk import aapt | |
13 | |
14 | |
15 _AAPT_PATH = os.path.join(constants.ANDROID_SDK_TOOLS, 'aapt') | |
16 _MANIFEST_ATTRIBUTE_RE = re.compile( | |
17 r'\s*A: ([^\(\)= ]*)\([^\(\)= ]*\)="(.*)" \(Raw: .*\)$') | |
18 _MANIFEST_ELEMENT_RE = re.compile(r'\s*(?:E|N): (\S*) .*$') | |
19 _PACKAGE_NAME_RE = re.compile(r'package: .*name=\'(\S*)\'') | |
20 _SPLIT_NAME_RE = re.compile(r'package: .*split=\'(\S*)\'') | |
21 | |
22 | |
23 def GetPackageName(apk_path): | |
24 """Returns the package name of the apk.""" | |
25 return ApkHelper(apk_path).GetPackageName() | |
26 | |
27 | |
28 # TODO(jbudorick): Deprecate and remove this function once callers have been | |
29 # converted to ApkHelper.GetInstrumentationName | |
30 def GetInstrumentationName(apk_path): | |
31 """Returns the name of the Instrumentation in the apk.""" | |
32 return ApkHelper(apk_path).GetInstrumentationName() | |
33 | |
34 | |
35 def _ParseManifestFromApk(apk_path): | |
36 aapt_output = aapt.Dump('xmltree', apk_path, 'AndroidManifest.xml') | |
37 | |
38 parsed_manifest = {} | |
39 node_stack = [parsed_manifest] | |
40 indent = ' ' | |
41 | |
42 for line in aapt_output[1:]: | |
43 if len(line) == 0: | |
44 continue | |
45 | |
46 indent_depth = 0 | |
47 while line[(len(indent) * indent_depth):].startswith(indent): | |
48 indent_depth += 1 | |
49 | |
50 node_stack = node_stack[:indent_depth] | |
51 node = node_stack[-1] | |
52 | |
53 m = _MANIFEST_ELEMENT_RE.match(line[len(indent) * indent_depth:]) | |
54 if m: | |
55 if not m.group(1) in node: | |
56 node[m.group(1)] = {} | |
57 node_stack += [node[m.group(1)]] | |
58 continue | |
59 | |
60 m = _MANIFEST_ATTRIBUTE_RE.match(line[len(indent) * indent_depth:]) | |
61 if m: | |
62 if not m.group(1) in node: | |
63 node[m.group(1)] = [] | |
64 node[m.group(1)].append(m.group(2)) | |
65 continue | |
66 | |
67 return parsed_manifest | |
68 | |
69 | |
70 class ApkHelper(object): | |
71 def __init__(self, apk_path): | |
72 self._apk_path = apk_path | |
73 self._manifest = None | |
74 self._package_name = None | |
75 self._split_name = None | |
76 | |
77 def GetActivityName(self): | |
78 """Returns the name of the Activity in the apk.""" | |
79 manifest_info = self._GetManifest() | |
80 try: | |
81 activity = ( | |
82 manifest_info['manifest']['application']['activity'] | |
83 ['android:name'][0]) | |
84 except KeyError: | |
85 return None | |
86 if '.' not in activity: | |
87 activity = '%s.%s' % (self.GetPackageName(), activity) | |
88 elif activity.startswith('.'): | |
89 activity = '%s%s' % (self.GetPackageName(), activity) | |
90 return activity | |
91 | |
92 def GetInstrumentationName( | |
93 self, default='android.test.InstrumentationTestRunner'): | |
94 """Returns the name of the Instrumentation in the apk.""" | |
95 manifest_info = self._GetManifest() | |
96 try: | |
97 return manifest_info['manifest']['instrumentation']['android:name'][0] | |
98 except KeyError: | |
99 return default | |
100 | |
101 def GetPackageName(self): | |
102 """Returns the package name of the apk.""" | |
103 if self._package_name: | |
104 return self._package_name | |
105 | |
106 aapt_output = aapt.Dump('badging', self._apk_path) | |
107 for line in aapt_output: | |
108 m = _PACKAGE_NAME_RE.match(line) | |
109 if m: | |
110 self._package_name = m.group(1) | |
111 return self._package_name | |
112 raise Exception('Failed to determine package name of %s' % self._apk_path) | |
113 | |
114 def GetSplitName(self): | |
115 """Returns the name of the split of the apk.""" | |
116 if self._split_name: | |
117 return self._split_name | |
118 | |
119 aapt_output = aapt.Dump('badging', self._apk_path) | |
120 for line in aapt_output: | |
121 m = _SPLIT_NAME_RE.match(line) | |
122 if m: | |
123 self._split_name = m.group(1) | |
124 return self._split_name | |
125 return None | |
126 | |
127 def _GetManifest(self): | |
128 if not self._manifest: | |
129 self._manifest = _ParseManifestFromApk(self._apk_path) | |
130 return self._manifest | |
131 | |
OLD | NEW |