• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/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
19from __future__ import print_function
20
21import argparse
22import json
23import sys
24
25from manifest import compare_version_gt
26
27
28def parse_args(args):
29    """Parse commandline arguments."""
30    parser = argparse.ArgumentParser()
31    parser.add_argument(
32        '--target-sdk-version',
33        default='',
34        dest='sdk',
35        help='specify target SDK version (as it appears in the manifest)')
36    parser.add_argument(
37        '--context-json',
38        default='',
39        dest='context_json',
40    )
41    parser.add_argument(
42        '--product-packages',
43        default='',
44        dest='product_packages_file',
45    )
46    return parser.parse_args(args)
47
48
49# Special keyword that means that the context should be added to class loader
50# context regardless of the target SDK version.
51any_sdk = 'any'
52
53context_sep = '#'
54
55
56def encode_class_loader(context, product_packages):
57    host_sub_contexts, target_sub_contexts = encode_class_loaders(
58            context['Subcontexts'], product_packages)
59
60    return ('PCL[%s]%s' % (context['Host'], host_sub_contexts),
61            'PCL[%s]%s' % (context['Device'], target_sub_contexts))
62
63
64def encode_class_loaders(contexts, product_packages):
65    host_contexts = []
66    target_contexts = []
67
68    for context in contexts:
69        if not context['Optional'] or context['Name'] in product_packages:
70            host_context, target_context = encode_class_loader(
71                    context, product_packages)
72            host_contexts.append(host_context)
73            target_contexts.append(target_context)
74
75    if host_contexts:
76        return ('{%s}' % context_sep.join(host_contexts),
77                '{%s}' % context_sep.join(target_contexts))
78    else:
79        return '', ''
80
81
82def construct_context_args(target_sdk, context_json, product_packages):
83    all_contexts = []
84
85    # CLC for different SDK versions should come in specific order that agrees
86    # with PackageManager. Since PackageManager processes SDK versions in
87    # ascending order and prepends compatibility libraries at the front, the
88    # required order is descending, except for any_sdk that has numerically
89    # the largest order, but must be the last one. Example of correct order:
90    # [30, 29, 28, any_sdk]. There are Python tests to ensure that someone
91    # doesn't change this by accident, but there is no way to guard against
92    # changes in the PackageManager, except for grepping logcat on the first
93    # boot for absence of the following messages:
94    #
95    #   `logcat | grep -E 'ClassLoaderContext [a-z ]+ mismatch`
96
97    for sdk, contexts in sorted(
98            ((sdk, contexts)
99             for sdk, contexts in context_json.items()
100             if sdk != any_sdk and compare_version_gt(sdk, target_sdk)),
101            key=lambda item: int(item[0]), reverse=True):
102        all_contexts += contexts
103
104    if any_sdk in context_json:
105        all_contexts += context_json[any_sdk]
106
107    host_contexts, target_contexts = encode_class_loaders(
108            all_contexts, product_packages)
109
110    return (
111        'class_loader_context_arg=--class-loader-context=PCL[]%s ; ' %
112        host_contexts +
113        'stored_class_loader_context_arg='
114        '--stored-class-loader-context=PCL[]%s'
115        % target_contexts)
116
117
118def main():
119    """Program entry point."""
120    try:
121        args = parse_args(sys.argv[1:])
122        if not args.sdk:
123            raise SystemExit('target sdk version is not set')
124
125        context_json = json.loads(args.context_json)
126        with open(args.product_packages_file, 'r') as f:
127            product_packages = set(line.strip() for line in f if line.strip())
128
129        print(construct_context_args(args.sdk, context_json, product_packages))
130
131    # pylint: disable=broad-except
132    except Exception as err:
133        print('error: ' + str(err), file=sys.stderr)
134        sys.exit(-1)
135
136
137if __name__ == '__main__':
138    main()
139