Use BOARD_API_LEVEL to define ro.board.api_level

GRF devices must define the API level of which the SoC is first
shipped by setting BOARD_SHIPPING_API_LEVEL. As this is a permanent
value, vendors may not change this value even if they implement new
features under the GRF policy.

BOARD_API_LEVEL can be optionally defined in this case to manually
set the api level of the vendor implementation.
The current api level will be set to `ro.board.api_level` property.

Bug: 176950752
Test: atest --host post_process_props_unittest
Change-Id: Ib126c1a622ded9848650f3f60c0f15005867272d
diff --git a/tools/post_process_props.py b/tools/post_process_props.py
index d8c9cb1..46bae29 100755
--- a/tools/post_process_props.py
+++ b/tools/post_process_props.py
@@ -42,7 +42,59 @@
   # default to "adb". That might not the right policy there, but it's better
   # to be explicit.
   if not prop_list.get_value("persist.sys.usb.config"):
-    prop_list.put("persist.sys.usb.config", "none");
+    prop_list.put("persist.sys.usb.config", "none")
+
+def validate_and_add_grf_props(prop_list, sdk_version):
+  """Validate GRF properties if exist.
+
+  If ro.board.first_api_level is defined, check if its value is valid for the
+  sdk version.
+  Also, validate the value of ro.board.api_level if defined. If the
+  ro.board.api_level property is not defined, define it with the required
+  vendor API level for the GRF policy.
+
+  Returns:
+    True if the GRF properties are valid.
+  """
+  grf_api_level = prop_list.get_value("ro.board.first_api_level")
+  board_api_level = prop_list.get_value("ro.board.api_level")
+
+  if not grf_api_level:
+    if board_api_level:
+      sys.stderr.write("error: non-GRF device must not define "
+                       "ro.board.api_level\n")
+      return False
+    # non-GRF device skips the GRF validation test
+    return True
+
+  grf_api_level = int(grf_api_level)
+  if grf_api_level > sdk_version:
+    sys.stderr.write("error: ro.board.first_api_level(%d) must be less than "
+                     "or equal to ro.build.version.sdk(%d)\n"
+                     % (grf_api_level, sdk_version))
+    return False
+
+  grf_window = 4
+  grf_required_api_level = (grf_api_level
+                  + grf_window * ((sdk_version - grf_api_level) // grf_window))
+
+  if board_api_level:
+    board_api_level = int(board_api_level)
+    if board_api_level < grf_api_level or board_api_level > sdk_version:
+      sys.stderr.write("error: ro.board.api_level(%d) must be neither less "
+                       "than ro.board.first_api_level(%d) nor greater than "
+                       "ro.build.version.sdk(%d)\n"
+                       % (board_api_level, grf_api_level, sdk_version))
+      return False
+    if board_api_level < grf_required_api_level:
+      sys.stderr.write("error: ro.board.api_level(%d) must be greater than or "
+                       "equal to %d based on GRF policy\n"
+                       % (board_api_level, grf_required_api_level))
+      return False
+  else:
+    prop_list.put("ro.board.api_level", str(grf_required_api_level))
+
+  return True
 
 def validate(prop_list):
   """Validate the properties.
@@ -215,6 +267,7 @@
                       default=False)
   parser.add_argument("filename")
   parser.add_argument("disallowed_keys", metavar="KEY", type=str, nargs="*")
+  parser.add_argument("--sdk-version", type=int, required=True)
   args = parser.parse_args()
 
   if not args.filename.endswith("/build.prop"):
@@ -225,6 +278,8 @@
   mangle_build_prop(props)
   if not override_optional_props(props, args.allow_dup):
     sys.exit(1)
+  if not validate_and_add_grf_props(props, args.sdk_version):
+    sys.exit(1)
   if not validate(props):
     sys.exit(1)