1 /*
2  * Copyright (C) 2024 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 #include <unistd.h>
17 
18 #include <regex>
19 
20 #include <android-base/logging.h>
21 #include <android-base/properties.h>
22 #include <android-base/strings.h>
23 
24 #if defined(__ANDROID__)
25 #include <log/log_properties.h>
26 #include "selinux/android.h"
27 #endif
28 
29 #if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
30 #include <com_android_tradeinmode_flags.h>
31 #endif
32 
33 static bool in_tradeinmode = false;
34 static constexpr char kTradeInModeProp[] = "persist.adb.tradeinmode";
35 
36 static constexpr int TIM_DISABLED = -1;
37 static constexpr int TIM_UNSET = 0;
38 static constexpr int TIM_FOYER = 1;
39 static constexpr int TIM_EVALUATION_MODE = 2;
40 
should_enter_tradeinmode()41 bool should_enter_tradeinmode() {
42 #if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
43     if (!com_android_tradeinmode_flags_enable_trade_in_mode()) {
44         return false;
45     }
46     return android::base::GetIntProperty(kTradeInModeProp, TIM_UNSET) == TIM_FOYER;
47 #else
48     return false;
49 #endif
50 }
51 
enter_tradeinmode(const char * seclabel)52 void enter_tradeinmode(const char* seclabel) {
53 #if defined(__ANDROID__)
54     if (selinux_android_setcon(seclabel) < 0) {
55         PLOG(ERROR) << "Could not set SELinux context";
56 
57         // Flag TIM as failed so we don't enter a restart loop.
58         android::base::SetProperty(kTradeInModeProp, std::to_string(TIM_DISABLED));
59 
60         _exit(1);
61     }
62 
63     // Keep a separate global flag for TIM in case the property changes (for
64     // example, if it's set while as root for testing).
65     in_tradeinmode = true;
66 #endif
67 }
68 
is_in_tradeinmode()69 bool is_in_tradeinmode() {
70     return in_tradeinmode;
71 }
72 
is_in_tradein_evaluation_mode()73 bool is_in_tradein_evaluation_mode() {
74     return android::base::GetIntProperty(kTradeInModeProp, TIM_UNSET) == TIM_EVALUATION_MODE;
75 }
76 
allow_tradeinmode_command(std::string_view name)77 bool allow_tradeinmode_command(std::string_view name) {
78 #if defined(__ANDROID__)
79     // Allow "adb root" from trade-in-mode so that automated testing is possible.
80     if (__android_log_is_debuggable() && android::base::ConsumePrefix(&name, "root:")) {
81         return true;
82     }
83 #endif
84 
85     // Allow "shell tradeinmode" with only simple arguments.
86     std::regex tim_pattern("shell[^:]*:tradeinmode(\\s*|\\s[A-Za-z0-9_\\-\\s]*)");
87     return std::regex_match(std::string(name), tim_pattern);
88 }
89