construct_context.py 4.4 KB

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