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