xref: /aosp_15_r20/tools/asuite/aidegen/sdk/android_sdk.py (revision c2e18aaa1096c836b086f94603d04f4eb9cf37f5)
1*c2e18aaaSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*c2e18aaaSAndroid Build Coastguard Worker#
3*c2e18aaaSAndroid Build Coastguard Worker# Copyright 2020 - The Android Open Source Project
4*c2e18aaaSAndroid Build Coastguard Worker#
5*c2e18aaaSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*c2e18aaaSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*c2e18aaaSAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*c2e18aaaSAndroid Build Coastguard Worker#
9*c2e18aaaSAndroid Build Coastguard Worker#     http://www.apache.org/licenses/LICENSE-2.0
10*c2e18aaaSAndroid Build Coastguard Worker#
11*c2e18aaaSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*c2e18aaaSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*c2e18aaaSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*c2e18aaaSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*c2e18aaaSAndroid Build Coastguard Worker# limitations under the License.
16*c2e18aaaSAndroid Build Coastguard Worker
17*c2e18aaaSAndroid Build Coastguard Worker"""Config Android SDK information.
18*c2e18aaaSAndroid Build Coastguard Worker
19*c2e18aaaSAndroid Build Coastguard WorkerIn order to create the configuration of Android SDK in IntelliJ automatically,
20*c2e18aaaSAndroid Build Coastguard Workerparses the Android SDK information from the Android SDK path.
21*c2e18aaaSAndroid Build Coastguard Worker
22*c2e18aaaSAndroid Build Coastguard WorkerUsage example:
23*c2e18aaaSAndroid Build Coastguard Worker    android_sdk = AndroidSDK()
24*c2e18aaaSAndroid Build Coastguard Worker    android_sdk.path_analysis(default_sdk_path)
25*c2e18aaaSAndroid Build Coastguard Worker    api_level = android_sdk.max_api_level
26*c2e18aaaSAndroid Build Coastguard Worker    android_sdk_path = android_sdk.android_sdk_path
27*c2e18aaaSAndroid Build Coastguard Worker    platform_mapping = android_sdk.platform_mapping
28*c2e18aaaSAndroid Build Coastguard Worker"""
29*c2e18aaaSAndroid Build Coastguard Worker
30*c2e18aaaSAndroid Build Coastguard Workerfrom __future__ import absolute_import
31*c2e18aaaSAndroid Build Coastguard Worker
32*c2e18aaaSAndroid Build Coastguard Workerimport glob
33*c2e18aaaSAndroid Build Coastguard Workerimport os
34*c2e18aaaSAndroid Build Coastguard Workerimport re
35*c2e18aaaSAndroid Build Coastguard Worker
36*c2e18aaaSAndroid Build Coastguard Workerfrom aidegen.lib import common_util
37*c2e18aaaSAndroid Build Coastguard Worker
38*c2e18aaaSAndroid Build Coastguard Worker
39*c2e18aaaSAndroid Build Coastguard Workerclass AndroidSDK:
40*c2e18aaaSAndroid Build Coastguard Worker    """Configures API level from the Android SDK path.
41*c2e18aaaSAndroid Build Coastguard Worker
42*c2e18aaaSAndroid Build Coastguard Worker    Attributes:
43*c2e18aaaSAndroid Build Coastguard Worker        _android_sdk_path: The path to the Android SDK,
44*c2e18aaaSAndroid Build Coastguard Worker                           None if the Android SDK doesn't exist.
45*c2e18aaaSAndroid Build Coastguard Worker        _max_api_level: An integer, the max API level in the platforms folder.
46*c2e18aaaSAndroid Build Coastguard Worker        _max_code_name: A string, the code name of the max API level.
47*c2e18aaaSAndroid Build Coastguard Worker        _max_folder_name: A string, the folder name corresponding
48*c2e18aaaSAndroid Build Coastguard Worker                          to the max API level.
49*c2e18aaaSAndroid Build Coastguard Worker        _platform_mapping: A dictionary of Android platform versions mapping to
50*c2e18aaaSAndroid Build Coastguard Worker                           the API level and the Android version code name. e.g.
51*c2e18aaaSAndroid Build Coastguard Worker                           {
52*c2e18aaaSAndroid Build Coastguard Worker                             'android-29': {
53*c2e18aaaSAndroid Build Coastguard Worker                               'api_level': 29,
54*c2e18aaaSAndroid Build Coastguard Worker                               'code_name': '29',
55*c2e18aaaSAndroid Build Coastguard Worker                             },
56*c2e18aaaSAndroid Build Coastguard Worker                             'android-Q': {
57*c2e18aaaSAndroid Build Coastguard Worker                               'api_level': 29,
58*c2e18aaaSAndroid Build Coastguard Worker                               'code_name': 'Q',
59*c2e18aaaSAndroid Build Coastguard Worker                             }
60*c2e18aaaSAndroid Build Coastguard Worker                           }
61*c2e18aaaSAndroid Build Coastguard Worker    """
62*c2e18aaaSAndroid Build Coastguard Worker
63*c2e18aaaSAndroid Build Coastguard Worker    _API_LEVEL = 'api_level'
64*c2e18aaaSAndroid Build Coastguard Worker    _CODE_NAME = 'code_name'
65*c2e18aaaSAndroid Build Coastguard Worker    _FOLDER_NAME = 'folder_name'
66*c2e18aaaSAndroid Build Coastguard Worker    _RE_API_LEVEL = re.compile(
67*c2e18aaaSAndroid Build Coastguard Worker        r'AndroidVersion\.ApiLevel=(?P<api_level>[\d]+)')
68*c2e18aaaSAndroid Build Coastguard Worker    _RE_CODE_NAME = re.compile(
69*c2e18aaaSAndroid Build Coastguard Worker        r'AndroidVersion\.CodeName=(?P<code_name>[a-zA-Z]+)')
70*c2e18aaaSAndroid Build Coastguard Worker    _GLOB_PROPERTIES_FILE = os.path.join('platforms', 'android-*',
71*c2e18aaaSAndroid Build Coastguard Worker                                         'source.properties')
72*c2e18aaaSAndroid Build Coastguard Worker    _INPUT_QUERY_TIMES = 3
73*c2e18aaaSAndroid Build Coastguard Worker    _ENTER_ANDROID_SDK_PATH = ('\nThe Android SDK folder:{} doesn\'t exist. '
74*c2e18aaaSAndroid Build Coastguard Worker                               'The debug function "Attach debugger to Android '
75*c2e18aaaSAndroid Build Coastguard Worker                               'process" is disabled without Android SDK in '
76*c2e18aaaSAndroid Build Coastguard Worker                               'IntelliJ or Android Studio. Please set it up '
77*c2e18aaaSAndroid Build Coastguard Worker                               'to enable the function. \nPlease enter the '
78*c2e18aaaSAndroid Build Coastguard Worker                               'absolute path to Android SDK:')
79*c2e18aaaSAndroid Build Coastguard Worker    _WARNING_NO_ANDROID_SDK = ('Please install the Android SDK, otherwise the '
80*c2e18aaaSAndroid Build Coastguard Worker                               'debug function "Attach debugger to Android '
81*c2e18aaaSAndroid Build Coastguard Worker                               'process" cannot be enabled in IntelliJ or '
82*c2e18aaaSAndroid Build Coastguard Worker                               'Android Studio.')
83*c2e18aaaSAndroid Build Coastguard Worker
84*c2e18aaaSAndroid Build Coastguard Worker    def __init__(self):
85*c2e18aaaSAndroid Build Coastguard Worker        """Initializes AndroidSDK."""
86*c2e18aaaSAndroid Build Coastguard Worker        self._max_api_level = 0
87*c2e18aaaSAndroid Build Coastguard Worker        self._max_code_name = None
88*c2e18aaaSAndroid Build Coastguard Worker        self._max_folder_name = None
89*c2e18aaaSAndroid Build Coastguard Worker        self._platform_mapping = {}
90*c2e18aaaSAndroid Build Coastguard Worker        self._android_sdk_path = None
91*c2e18aaaSAndroid Build Coastguard Worker
92*c2e18aaaSAndroid Build Coastguard Worker    @property
93*c2e18aaaSAndroid Build Coastguard Worker    def max_api_level(self):
94*c2e18aaaSAndroid Build Coastguard Worker        """Gets the max API level."""
95*c2e18aaaSAndroid Build Coastguard Worker        return self._max_api_level
96*c2e18aaaSAndroid Build Coastguard Worker
97*c2e18aaaSAndroid Build Coastguard Worker    @property
98*c2e18aaaSAndroid Build Coastguard Worker    def max_code_name(self):
99*c2e18aaaSAndroid Build Coastguard Worker        """Gets the max code name."""
100*c2e18aaaSAndroid Build Coastguard Worker        return self._max_code_name
101*c2e18aaaSAndroid Build Coastguard Worker
102*c2e18aaaSAndroid Build Coastguard Worker    @property
103*c2e18aaaSAndroid Build Coastguard Worker    def max_folder_name(self):
104*c2e18aaaSAndroid Build Coastguard Worker        """Gets the max folder name."""
105*c2e18aaaSAndroid Build Coastguard Worker        return self._max_folder_name
106*c2e18aaaSAndroid Build Coastguard Worker
107*c2e18aaaSAndroid Build Coastguard Worker    @property
108*c2e18aaaSAndroid Build Coastguard Worker    def platform_mapping(self):
109*c2e18aaaSAndroid Build Coastguard Worker        """Gets the Android platform mapping."""
110*c2e18aaaSAndroid Build Coastguard Worker        return self._platform_mapping
111*c2e18aaaSAndroid Build Coastguard Worker
112*c2e18aaaSAndroid Build Coastguard Worker    @property
113*c2e18aaaSAndroid Build Coastguard Worker    def android_sdk_path(self):
114*c2e18aaaSAndroid Build Coastguard Worker        """Gets the Android SDK path."""
115*c2e18aaaSAndroid Build Coastguard Worker        return self._android_sdk_path
116*c2e18aaaSAndroid Build Coastguard Worker
117*c2e18aaaSAndroid Build Coastguard Worker    def _parse_max_api_level(self):
118*c2e18aaaSAndroid Build Coastguard Worker        """Parses the max API level from self._platform_mapping.
119*c2e18aaaSAndroid Build Coastguard Worker
120*c2e18aaaSAndroid Build Coastguard Worker        Returns:
121*c2e18aaaSAndroid Build Coastguard Worker            An integer of API level and 0 means no Android platform exists.
122*c2e18aaaSAndroid Build Coastguard Worker        """
123*c2e18aaaSAndroid Build Coastguard Worker        return max(
124*c2e18aaaSAndroid Build Coastguard Worker            [v[self._API_LEVEL] for v in self._platform_mapping.values()],
125*c2e18aaaSAndroid Build Coastguard Worker            default=0)
126*c2e18aaaSAndroid Build Coastguard Worker
127*c2e18aaaSAndroid Build Coastguard Worker    def _parse_max_code_name(self):
128*c2e18aaaSAndroid Build Coastguard Worker        """Parses the max code name from self._platform_mapping.
129*c2e18aaaSAndroid Build Coastguard Worker
130*c2e18aaaSAndroid Build Coastguard Worker        Returns:
131*c2e18aaaSAndroid Build Coastguard Worker            A string of code name.
132*c2e18aaaSAndroid Build Coastguard Worker        """
133*c2e18aaaSAndroid Build Coastguard Worker        code_name = ''
134*c2e18aaaSAndroid Build Coastguard Worker        for data in self._platform_mapping.values():
135*c2e18aaaSAndroid Build Coastguard Worker            if (data[self._API_LEVEL] == self._max_api_level
136*c2e18aaaSAndroid Build Coastguard Worker                    and data[self._CODE_NAME] > code_name):
137*c2e18aaaSAndroid Build Coastguard Worker                code_name = data[self._CODE_NAME]
138*c2e18aaaSAndroid Build Coastguard Worker        return code_name
139*c2e18aaaSAndroid Build Coastguard Worker
140*c2e18aaaSAndroid Build Coastguard Worker    def _get_max_folder_name(self):
141*c2e18aaaSAndroid Build Coastguard Worker        """Gets the max folder name from self._platform_mapping.
142*c2e18aaaSAndroid Build Coastguard Worker
143*c2e18aaaSAndroid Build Coastguard Worker        Returns:
144*c2e18aaaSAndroid Build Coastguard Worker            A string of the folder name corresponding to the max API level.
145*c2e18aaaSAndroid Build Coastguard Worker        """
146*c2e18aaaSAndroid Build Coastguard Worker        folder_name = ''
147*c2e18aaaSAndroid Build Coastguard Worker        for platform, data in self._platform_mapping.items():
148*c2e18aaaSAndroid Build Coastguard Worker            if (data[self._API_LEVEL] == self.max_api_level
149*c2e18aaaSAndroid Build Coastguard Worker                    and data[self._CODE_NAME] == self._max_code_name):
150*c2e18aaaSAndroid Build Coastguard Worker                folder_name = platform
151*c2e18aaaSAndroid Build Coastguard Worker                break
152*c2e18aaaSAndroid Build Coastguard Worker        return folder_name
153*c2e18aaaSAndroid Build Coastguard Worker
154*c2e18aaaSAndroid Build Coastguard Worker    def _parse_api_info(self, properties_file):
155*c2e18aaaSAndroid Build Coastguard Worker        """Parses the API information from the source.properties file.
156*c2e18aaaSAndroid Build Coastguard Worker
157*c2e18aaaSAndroid Build Coastguard Worker        For the preview platform like android-Q, the source.properties file
158*c2e18aaaSAndroid Build Coastguard Worker        contains two properties named AndroidVersion.ApiLevel, API level of
159*c2e18aaaSAndroid Build Coastguard Worker        the platform, and AndroidVersion.CodeName such as Q, the code name of
160*c2e18aaaSAndroid Build Coastguard Worker        the platform.
161*c2e18aaaSAndroid Build Coastguard Worker        However, the formal platform like android-29, there is no property
162*c2e18aaaSAndroid Build Coastguard Worker        AndroidVersion.CodeName.
163*c2e18aaaSAndroid Build Coastguard Worker
164*c2e18aaaSAndroid Build Coastguard Worker        Args:
165*c2e18aaaSAndroid Build Coastguard Worker            properties_file: A path of the source.properties file.
166*c2e18aaaSAndroid Build Coastguard Worker
167*c2e18aaaSAndroid Build Coastguard Worker        Returns:
168*c2e18aaaSAndroid Build Coastguard Worker            A tuple contains the API level and Code name of the
169*c2e18aaaSAndroid Build Coastguard Worker            source.properties file.
170*c2e18aaaSAndroid Build Coastguard Worker            API level: An integer of the platform, e.g. 29.
171*c2e18aaaSAndroid Build Coastguard Worker            Code name: A string, e.g. 29 or Q.
172*c2e18aaaSAndroid Build Coastguard Worker        """
173*c2e18aaaSAndroid Build Coastguard Worker        api_level = 0
174*c2e18aaaSAndroid Build Coastguard Worker        properties = common_util.read_file_content(properties_file)
175*c2e18aaaSAndroid Build Coastguard Worker        match_api_level = self._RE_API_LEVEL.search(properties)
176*c2e18aaaSAndroid Build Coastguard Worker        if match_api_level:
177*c2e18aaaSAndroid Build Coastguard Worker            api_level = match_api_level.group(self._API_LEVEL)
178*c2e18aaaSAndroid Build Coastguard Worker        match_code_name = self._RE_CODE_NAME.search(properties)
179*c2e18aaaSAndroid Build Coastguard Worker        if match_code_name:
180*c2e18aaaSAndroid Build Coastguard Worker            code_name = match_code_name.group(self._CODE_NAME)
181*c2e18aaaSAndroid Build Coastguard Worker        else:
182*c2e18aaaSAndroid Build Coastguard Worker            code_name = api_level
183*c2e18aaaSAndroid Build Coastguard Worker        return api_level, code_name
184*c2e18aaaSAndroid Build Coastguard Worker
185*c2e18aaaSAndroid Build Coastguard Worker    def _gen_platform_mapping(self, path):
186*c2e18aaaSAndroid Build Coastguard Worker        """Generates the Android platforms mapping.
187*c2e18aaaSAndroid Build Coastguard Worker
188*c2e18aaaSAndroid Build Coastguard Worker        Args:
189*c2e18aaaSAndroid Build Coastguard Worker            path: A string, the absolute path of Android SDK.
190*c2e18aaaSAndroid Build Coastguard Worker
191*c2e18aaaSAndroid Build Coastguard Worker        Returns:
192*c2e18aaaSAndroid Build Coastguard Worker            True when successful generates platform mapping, otherwise False.
193*c2e18aaaSAndroid Build Coastguard Worker        """
194*c2e18aaaSAndroid Build Coastguard Worker        prop_files = glob.glob(os.path.join(path, self._GLOB_PROPERTIES_FILE))
195*c2e18aaaSAndroid Build Coastguard Worker        for prop_file in prop_files:
196*c2e18aaaSAndroid Build Coastguard Worker            api_level, code_name = self._parse_api_info(prop_file)
197*c2e18aaaSAndroid Build Coastguard Worker            if not api_level:
198*c2e18aaaSAndroid Build Coastguard Worker                continue
199*c2e18aaaSAndroid Build Coastguard Worker            platform = os.path.basename(os.path.dirname(prop_file))
200*c2e18aaaSAndroid Build Coastguard Worker            self._platform_mapping[platform] = {
201*c2e18aaaSAndroid Build Coastguard Worker                self._API_LEVEL: int(api_level),
202*c2e18aaaSAndroid Build Coastguard Worker                self._CODE_NAME: code_name
203*c2e18aaaSAndroid Build Coastguard Worker            }
204*c2e18aaaSAndroid Build Coastguard Worker        return bool(self._platform_mapping)
205*c2e18aaaSAndroid Build Coastguard Worker
206*c2e18aaaSAndroid Build Coastguard Worker    def is_android_sdk_path(self, path):
207*c2e18aaaSAndroid Build Coastguard Worker        """Checks if the Android SDK path is correct.
208*c2e18aaaSAndroid Build Coastguard Worker
209*c2e18aaaSAndroid Build Coastguard Worker        Confirm the Android SDK path is correct by checking if it has
210*c2e18aaaSAndroid Build Coastguard Worker        platform versions.
211*c2e18aaaSAndroid Build Coastguard Worker
212*c2e18aaaSAndroid Build Coastguard Worker        Args:
213*c2e18aaaSAndroid Build Coastguard Worker            path: A string, the path of Android SDK user input.
214*c2e18aaaSAndroid Build Coastguard Worker
215*c2e18aaaSAndroid Build Coastguard Worker        Returns:
216*c2e18aaaSAndroid Build Coastguard Worker            True when get a platform version, otherwise False.
217*c2e18aaaSAndroid Build Coastguard Worker        """
218*c2e18aaaSAndroid Build Coastguard Worker        if self._gen_platform_mapping(path):
219*c2e18aaaSAndroid Build Coastguard Worker            self._android_sdk_path = path
220*c2e18aaaSAndroid Build Coastguard Worker            self._max_api_level = self._parse_max_api_level()
221*c2e18aaaSAndroid Build Coastguard Worker            self._max_code_name = self._parse_max_code_name()
222*c2e18aaaSAndroid Build Coastguard Worker            self._max_folder_name = self._get_max_folder_name()
223*c2e18aaaSAndroid Build Coastguard Worker            return True
224*c2e18aaaSAndroid Build Coastguard Worker        return False
225*c2e18aaaSAndroid Build Coastguard Worker
226*c2e18aaaSAndroid Build Coastguard Worker    def path_analysis(self, sdk_path):
227*c2e18aaaSAndroid Build Coastguard Worker        """Analyses the Android SDK path.
228*c2e18aaaSAndroid Build Coastguard Worker
229*c2e18aaaSAndroid Build Coastguard Worker        Confirm the path is an Android SDK folder. If it's not correct, ask user
230*c2e18aaaSAndroid Build Coastguard Worker        to enter a new one. Skip asking when enter nothing.
231*c2e18aaaSAndroid Build Coastguard Worker
232*c2e18aaaSAndroid Build Coastguard Worker        Args:
233*c2e18aaaSAndroid Build Coastguard Worker            sdk_path: A string, the path of Android SDK.
234*c2e18aaaSAndroid Build Coastguard Worker
235*c2e18aaaSAndroid Build Coastguard Worker        Returns:
236*c2e18aaaSAndroid Build Coastguard Worker            True when get an Android SDK path, otherwise False.
237*c2e18aaaSAndroid Build Coastguard Worker        """
238*c2e18aaaSAndroid Build Coastguard Worker        for _ in range(self._INPUT_QUERY_TIMES):
239*c2e18aaaSAndroid Build Coastguard Worker            if self.is_android_sdk_path(sdk_path):
240*c2e18aaaSAndroid Build Coastguard Worker                return True
241*c2e18aaaSAndroid Build Coastguard Worker            sdk_path = input(common_util.COLORED_FAIL(
242*c2e18aaaSAndroid Build Coastguard Worker                self._ENTER_ANDROID_SDK_PATH.format(sdk_path)))
243*c2e18aaaSAndroid Build Coastguard Worker            if not sdk_path:
244*c2e18aaaSAndroid Build Coastguard Worker                break
245*c2e18aaaSAndroid Build Coastguard Worker        print('\n{} {}\n'.format(common_util.COLORED_INFO('Warning:'),
246*c2e18aaaSAndroid Build Coastguard Worker                                 self._WARNING_NO_ANDROID_SDK))
247*c2e18aaaSAndroid Build Coastguard Worker        return False
248