blob: 882c2dbf5f55ae541ff0388b561d1081b3f696ee [file] [log] [blame]
Ulya Trafimovich5f364b62020-06-30 12:39:01 +01001#!/usr/bin/env python
2#
3# Copyright (C) 2020 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17"""A tool for constructing class loader context."""
18
Ulya Trafimovich5f364b62020-06-30 12:39:01 +010019import argparse
Jiakai Zhanga4496782023-05-17 16:57:30 +010020import json
Ulya Trafimovich5f364b62020-06-30 12:39:01 +010021import sys
22
23from manifest import compare_version_gt
24
25
26def parse_args(args):
Spandan Dasec555f12021-08-25 18:42:40 +000027 """Parse commandline arguments."""
28 parser = argparse.ArgumentParser()
29 parser.add_argument(
30 '--target-sdk-version',
31 default='',
32 dest='sdk',
33 help='specify target SDK version (as it appears in the manifest)')
34 parser.add_argument(
Jiakai Zhanga4496782023-05-17 16:57:30 +010035 '--context-json',
36 default='',
37 dest='context_json',
38 )
Spandan Dasec555f12021-08-25 18:42:40 +000039 parser.add_argument(
Jiakai Zhanga4496782023-05-17 16:57:30 +010040 '--product-packages',
41 default='',
42 dest='product_packages_file',
Spandan Dasec555f12021-08-25 18:42:40 +000043 )
44 return parser.parse_args(args)
45
Ulya Trafimovich5f364b62020-06-30 12:39:01 +010046
Ulya Trafimovich8130c482020-10-07 15:17:13 +010047# Special keyword that means that the context should be added to class loader
Ulya Trafimovich5f364b62020-06-30 12:39:01 +010048# context regardless of the target SDK version.
49any_sdk = 'any'
50
Jiakai Zhanga4496782023-05-17 16:57:30 +010051context_sep = '#'
Spandan Dasec555f12021-08-25 18:42:40 +000052
Ulya Trafimovich5f364b62020-06-30 12:39:01 +010053
Jiakai Zhanga4496782023-05-17 16:57:30 +010054def encode_class_loader(context, product_packages):
55 host_sub_contexts, target_sub_contexts = encode_class_loaders(
56 context['Subcontexts'], product_packages)
57
58 return ('PCL[%s]%s' % (context['Host'], host_sub_contexts),
59 'PCL[%s]%s' % (context['Device'], target_sub_contexts))
60
61
62def encode_class_loaders(contexts, product_packages):
63 host_contexts = []
64 target_contexts = []
65
66 for context in contexts:
67 if not context['Optional'] or context['Name'] in product_packages:
68 host_context, target_context = encode_class_loader(
69 context, product_packages)
70 host_contexts.append(host_context)
71 target_contexts.append(target_context)
72
73 if host_contexts:
74 return ('{%s}' % context_sep.join(host_contexts),
75 '{%s}' % context_sep.join(target_contexts))
76 else:
77 return '', ''
78
79
80def construct_context_args(target_sdk, context_json, product_packages):
81 all_contexts = []
82
83 # CLC for different SDK versions should come in specific order that agrees
84 # with PackageManager. Since PackageManager processes SDK versions in
85 # ascending order and prepends compatibility libraries at the front, the
86 # required order is descending, except for any_sdk that has numerically
87 # the largest order, but must be the last one. Example of correct order:
88 # [30, 29, 28, any_sdk]. There are Python tests to ensure that someone
89 # doesn't change this by accident, but there is no way to guard against
90 # changes in the PackageManager, except for grepping logcat on the first
91 # boot for absence of the following messages:
92 #
93 # `logcat | grep -E 'ClassLoaderContext [a-z ]+ mismatch`
94
95 for sdk, contexts in sorted(
96 ((sdk, contexts)
97 for sdk, contexts in context_json.items()
98 if sdk != any_sdk and compare_version_gt(sdk, target_sdk)),
99 key=lambda item: int(item[0]), reverse=True):
100 all_contexts += contexts
101
102 if any_sdk in context_json:
103 all_contexts += context_json[any_sdk]
104
105 host_contexts, target_contexts = encode_class_loaders(
106 all_contexts, product_packages)
107
Spandan Dasec555f12021-08-25 18:42:40 +0000108 return (
Jiakai Zhanga4496782023-05-17 16:57:30 +0100109 'class_loader_context_arg=--class-loader-context=PCL[]%s ; ' %
110 host_contexts +
111 'stored_class_loader_context_arg='
112 '--stored-class-loader-context=PCL[]%s'
113 % target_contexts)
Spandan Dasec555f12021-08-25 18:42:40 +0000114
Ulya Trafimovich5f364b62020-06-30 12:39:01 +0100115
116def main():
Spandan Dasec555f12021-08-25 18:42:40 +0000117 """Program entry point."""
118 try:
119 args = parse_args(sys.argv[1:])
120 if not args.sdk:
121 raise SystemExit('target sdk version is not set')
Ulya Trafimovich5f364b62020-06-30 12:39:01 +0100122
Jiakai Zhanga4496782023-05-17 16:57:30 +0100123 context_json = json.loads(args.context_json)
124 with open(args.product_packages_file, 'r') as f:
125 product_packages = set(line.strip() for line in f if line.strip())
126
127 print(construct_context_args(args.sdk, context_json, product_packages))
Ulya Trafimovich5f364b62020-06-30 12:39:01 +0100128
Spandan Dasec555f12021-08-25 18:42:40 +0000129 # pylint: disable=broad-except
130 except Exception as err:
131 print('error: ' + str(err), file=sys.stderr)
132 sys.exit(-1)
133
Ulya Trafimovich5f364b62020-06-30 12:39:01 +0100134
135if __name__ == '__main__':
Spandan Dasec555f12021-08-25 18:42:40 +0000136 main()