1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #pragma once
18
19 #include <assert.h>
20 #include <ctype.h>
21 #include <limits.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <android/api-level.h>
26 #include <log/log.h>
27 #include <sys/system_properties.h>
28
29 #include "sdk_level.h"
30
31 namespace android {
32 namespace modules {
33 namespace sdklevel {
34 namespace unbounded {
35
getVersionInt(const char * version)36 inline auto getVersionInt(const char *version) {
37 LOG_ALWAYS_FATAL_IF(version[0] == '\0', "empty version");
38 char *next_char = 0;
39 const long versionInt = strtol(version, &next_char, 10);
40 LOG_ALWAYS_FATAL_IF(*next_char != '\0', "no conversion from \"%s\" to long",
41 version);
42 LOG_ALWAYS_FATAL_IF(versionInt <= 0, "negative version: %s", version);
43 LOG_ALWAYS_FATAL_IF(versionInt > INT_MAX, "version too large: %s", version);
44 return (int)versionInt;
45 }
46
isCodename(const char * version)47 inline bool isCodename(const char *version) {
48 LOG_ALWAYS_FATAL_IF(version[0] == '\0', "empty version");
49 return isupper(version[0]);
50 }
51
52 struct SdkLevelAndCodenames {
53 int sdk_level;
54 const char *codenames;
55 };
56
57 static constexpr SdkLevelAndCodenames PREVIOUS_CODENAMES[] = {
58 {29, "Q"}, {30, "Q,R"}, {31, "Q,R,S"}, {32, "Q,R,S,Sv2"}};
59
getPreviousCodenames(int sdk_level)60 static char *getPreviousCodenames(int sdk_level) {
61 for (size_t i = 0;
62 i < sizeof(PREVIOUS_CODENAMES) / sizeof(PREVIOUS_CODENAMES[0]); ++i) {
63 if (sdk_level == PREVIOUS_CODENAMES[i].sdk_level) {
64 return strdup(PREVIOUS_CODENAMES[i].codenames);
65 }
66 }
67 return strdup("");
68 }
69
getKnownCodenames()70 static char *getKnownCodenames() {
71 const prop_info *pi =
72 __system_property_find("ro.build.version.known_codenames");
73 LOG_ALWAYS_FATAL_IF(pi == nullptr, "known_codenames property doesn't exist");
74 char *codenames = nullptr;
75 // The length of this property is not limited to PROP_VALUE_MAX; therefore it
76 // cannot be requested via __system_property_get
77 __system_property_read_callback(
78 pi,
79 [](void *cookie, const char *, const char *value, unsigned) {
80 auto codenames_ptr = static_cast<const char **>(cookie);
81 *codenames_ptr = strdup(value);
82 },
83 &codenames);
84 return codenames;
85 }
86
87 // Checks if input version is same/previous codename of one running on device.
isKnownCodename(const char * version)88 static bool isKnownCodename(const char *version) {
89 LOG_ALWAYS_FATAL_IF(!isCodename(version), "input version is not a codename");
90 char *const known_codenames =
91 IsAtLeastT() ? getKnownCodenames()
92 : getPreviousCodenames(android_get_device_api_level());
93 LOG_ALWAYS_FATAL_IF(known_codenames == nullptr, "null for known codenames");
94 char *p, *saveptr = known_codenames;
95 bool found = false;
96 // The example of known_codenames is Q,R,S,Sv2 (versions split by ',')
97 while (!found && (p = strtok_r(saveptr, ",", &saveptr))) {
98 if (strcmp(version, p) == 0) {
99 found = true;
100 }
101 }
102 free(known_codenames);
103 return found;
104 }
105
106 // Checks if the device is running a specific version or newer.
107 // Always use specific methods IsAtLeast*() available in sdk_level.h when the
108 // version is known at build time. This should only be used when a dynamic
109 // runtime check is needed.
IsAtLeast(const char * version)110 inline bool IsAtLeast(const char *version) {
111 char device_codename[PROP_VALUE_MAX];
112 detail::GetCodename(device_codename);
113 if (!strcmp("REL", device_codename)) {
114 if (isCodename(version)) {
115 LOG_ALWAYS_FATAL_IF(
116 isKnownCodename(version),
117 "Artifact with a known codename "
118 "%s must be recompiled with a finalized integer version.",
119 version);
120 return false;
121 }
122 return android_get_device_api_level() >= getVersionInt(version);
123 }
124 if (isCodename(version)) {
125 return isKnownCodename(version);
126 }
127 return android_get_device_api_level() >= getVersionInt(version);
128 }
129
130 // Checks if the device is running a specific version or older.
131 // Always use specific methods IsAtLeast*() available in sdk_level.h when the
132 // version is known at build time. This should only be used when a dynamic
133 // runtime check is needed.
IsAtMost(const char * version)134 inline bool IsAtMost(const char *version) {
135 char device_codename[PROP_VALUE_MAX];
136 detail::GetCodename(device_codename);
137 if (!strcmp("REL", device_codename)) {
138 if (isCodename(version)) {
139 LOG_ALWAYS_FATAL_IF(
140 isKnownCodename(version),
141 "Artifact with a known codename "
142 "%s must be recompiled with a finalized integer version.",
143 version);
144 return true;
145 }
146 return android_get_device_api_level() <= getVersionInt(version);
147 }
148 if (isCodename(version)) {
149 return !isKnownCodename(version) || strcmp(version, device_codename) == 0;
150 }
151 return android_get_device_api_level() < getVersionInt(version);
152 }
153
154 } // namespace unbounded
155 } // namespace sdklevel
156 } // namespace modules
157 } // namespace android
158