1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
|
//
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "update_engine/aosp/hardware_android.h"
#include <sys/types.h>
#include <memory>
#include <string>
#include <string_view>
#include <android/sysprop/GkiProperties.sysprop.h>
#include <android-base/properties.h>
#include <base/files/file_util.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <bootloader_message/bootloader_message.h>
#include <fstab/fstab.h>
#include <libavb/libavb.h>
#include <libavb_user/avb_ops_user.h>
#include "update_engine/common/error_code_utils.h"
#include "update_engine/common/hardware.h"
#include "update_engine/common/platform_constants.h"
#include "update_engine/common/utils.h"
#ifndef __ANDROID_RECOVERY__
#include <android/sysprop/OtaProperties.sysprop.h>
#endif
using android::base::GetBoolProperty;
using android::base::GetIntProperty;
using android::base::GetProperty;
using std::string;
namespace chromeos_update_engine {
namespace {
// Android properties that identify the hardware and potentially non-updatable
// parts of the bootloader (such as the bootloader version and the baseband
// version).
const char kPropProductManufacturer[] = "ro.product.manufacturer";
const char kPropBootHardwareSKU[] = "ro.boot.hardware.sku";
const char kPropBootRevision[] = "ro.boot.revision";
const char kPropBuildDateUTC[] = "ro.build.date.utc";
string GetPartitionBuildDate(const string& partition_name) {
return android::base::GetProperty("ro." + partition_name + ".build.date.utc",
"");
}
ErrorCode IsTimestampNewerLogged(const std::string& partition_name,
const std::string& old_version,
const std::string& new_version) {
auto error_code = utils::IsTimestampNewer(old_version, new_version);
if (error_code != ErrorCode::kSuccess) {
LOG(WARNING) << "Timestamp check failed with "
<< utils::ErrorCodeToString(error_code) << ": "
<< partition_name << " Partition timestamp: " << old_version
<< " Update timestamp: " << new_version;
}
return error_code;
}
void SetVbmetaDigestProp(const std::string& value) {
#ifndef __ANDROID_RECOVERY__
if (!android::sysprop::OtaProperties::other_vbmeta_digest(value)) {
LOG(WARNING) << "Failed to set other vbmeta digest to " << value;
}
#endif
}
std::string CalculateVbmetaDigestForInactiveSlot() {
AvbSlotVerifyData* avb_slot_data;
auto suffix = fs_mgr_get_other_slot_suffix();
const char* requested_partitions[] = {nullptr};
auto avb_ops = avb_ops_user_new();
auto verify_result = avb_slot_verify(avb_ops,
requested_partitions,
suffix.c_str(),
AVB_SLOT_VERIFY_FLAGS_NONE,
AVB_HASHTREE_ERROR_MODE_EIO,
&avb_slot_data);
if (verify_result != AVB_SLOT_VERIFY_RESULT_OK) {
LOG(WARNING) << "Failed to verify avb slot data: " << verify_result;
return "";
}
uint8_t vbmeta_digest[AVB_SHA256_DIGEST_SIZE];
avb_slot_verify_data_calculate_vbmeta_digest(
avb_slot_data, AVB_DIGEST_TYPE_SHA256, vbmeta_digest);
std::string encoded_digest =
base::HexEncode(vbmeta_digest, AVB_SHA256_DIGEST_SIZE);
return base::ToLowerASCII(encoded_digest);
}
} // namespace
namespace hardware {
// Factory defined in hardware.h.
std::unique_ptr<HardwareInterface> CreateHardware() {
return std::make_unique<HardwareAndroid>();
}
} // namespace hardware
// In Android there are normally three kinds of builds: eng, userdebug and user.
// These builds target respectively a developer build, a debuggable version of
// the final product and the pristine final product the end user will run.
// Apart from the ro.build.type property name, they differ in the following
// properties that characterize the builds:
// * eng builds: ro.secure=0 and ro.debuggable=1
// * userdebug builds: ro.secure=1 and ro.debuggable=1
// * user builds: ro.secure=1 and ro.debuggable=0
//
// See IsOfficialBuild() and IsNormalMode() for the meaning of these options in
// Android.
bool HardwareAndroid::IsOfficialBuild() const {
// We run an official build iff ro.secure == 1, because we expect the build to
// behave like the end user product and check for updates. Note that while
// developers are able to build "official builds" by just running "make user",
// that will only result in a more restrictive environment. The important part
// is that we don't produce and push "non-official" builds to the end user.
//
// In case of a non-bool value, we take the most restrictive option and
// assume we are in an official-build.
return GetBoolProperty("ro.secure", true);
}
bool HardwareAndroid::IsNormalBootMode() const {
// We are running in "dev-mode" iff ro.debuggable == 1. In dev-mode the
// update_engine will allow extra developers options, such as providing a
// different update URL. In case of error, we assume the build is in
// normal-mode.
return !GetBoolProperty("ro.debuggable", false);
}
bool HardwareAndroid::AreDevFeaturesEnabled() const {
return !IsNormalBootMode();
}
bool HardwareAndroid::IsOOBEEnabled() const {
// No OOBE flow blocking updates for Android-based boards.
return false;
}
bool HardwareAndroid::IsOOBEComplete(base::Time* out_time_of_oobe) const {
LOG(WARNING) << "OOBE is not enabled but IsOOBEComplete() called.";
if (out_time_of_oobe)
*out_time_of_oobe = base::Time();
return true;
}
string HardwareAndroid::GetHardwareClass() const {
auto manufacturer = GetProperty(kPropProductManufacturer, "");
auto sku = GetProperty(kPropBootHardwareSKU, "");
auto revision = GetProperty(kPropBootRevision, "");
return manufacturer + ":" + sku + ":" + revision;
}
string HardwareAndroid::GetDeviceRequisition() const {
LOG(WARNING) << "STUB: Getting requisition is not supported.";
return "";
}
int HardwareAndroid::GetMinKernelKeyVersion() const {
LOG(WARNING) << "STUB: No Kernel key version is available.";
return -1;
}
int HardwareAndroid::GetMinFirmwareKeyVersion() const {
LOG(WARNING) << "STUB: No Firmware key version is available.";
return -1;
}
int HardwareAndroid::GetMaxFirmwareKeyRollforward() const {
LOG(WARNING) << "STUB: Getting firmware_max_rollforward is not supported.";
return -1;
}
bool HardwareAndroid::SetMaxFirmwareKeyRollforward(
int firmware_max_rollforward) {
LOG(WARNING) << "STUB: Setting firmware_max_rollforward is not supported.";
return false;
}
bool HardwareAndroid::SetMaxKernelKeyRollforward(int kernel_max_rollforward) {
LOG(WARNING) << "STUB: Setting kernel_max_rollforward is not supported.";
return false;
}
int HardwareAndroid::GetPowerwashCount() const {
LOG(WARNING) << "STUB: Assuming no factory reset was performed.";
return 0;
}
bool HardwareAndroid::SchedulePowerwash(bool save_rollback_data) {
LOG(INFO) << "Scheduling a powerwash to BCB.";
LOG_IF(WARNING, save_rollback_data) << "save_rollback_data was true but "
<< "isn't supported.";
string err;
if (!update_bootloader_message({"--wipe_data", "--reason=wipe_data_from_ota"},
&err)) {
LOG(ERROR) << "Failed to update bootloader message: " << err;
return false;
}
return true;
}
bool HardwareAndroid::CancelPowerwash() {
string err;
if (!clear_bootloader_message(&err)) {
LOG(ERROR) << "Failed to clear bootloader message: " << err;
return false;
}
return true;
}
bool HardwareAndroid::GetNonVolatileDirectory(base::FilePath* path) const {
base::FilePath local_path(constants::kNonVolatileDirectory);
if (!base::DirectoryExists(local_path)) {
LOG(ERROR) << "Non-volatile directory not found: " << local_path.value();
return false;
}
*path = local_path;
return true;
}
bool HardwareAndroid::GetPowerwashSafeDirectory(base::FilePath* path) const {
// On Android, we don't have a directory persisted across powerwash.
return false;
}
int64_t HardwareAndroid::GetBuildTimestamp() const {
return GetIntProperty<int64_t>(kPropBuildDateUTC, 0);
}
// Returns true if the device runs an userdebug build, and explicitly allows OTA
// downgrade.
bool HardwareAndroid::AllowDowngrade() const {
return GetBoolProperty("ro.ota.allow_downgrade", false) &&
GetBoolProperty("ro.debuggable", false);
}
bool HardwareAndroid::GetFirstActiveOmahaPingSent() const {
LOG(WARNING) << "STUB: Assuming first active omaha was never set.";
return false;
}
bool HardwareAndroid::SetFirstActiveOmahaPingSent() {
LOG(WARNING) << "STUB: Assuming first active omaha is set.";
// We will set it true, so its failure doesn't cause escalation.
return true;
}
void HardwareAndroid::SetWarmReset(bool warm_reset) {
if constexpr (!constants::kIsRecovery) {
constexpr char warm_reset_prop[] = "ota.warm_reset";
if (!android::base::SetProperty(warm_reset_prop, warm_reset ? "1" : "0")) {
LOG(WARNING) << "Failed to set prop " << warm_reset_prop;
}
}
}
void HardwareAndroid::SetVbmetaDigestForInactiveSlot(bool reset) {
if constexpr (constants::kIsRecovery) {
return;
}
if (android::base::GetProperty("ro.boot.avb_version", "").empty() &&
android::base::GetProperty("ro.boot.vbmeta.avb_version", "").empty()) {
LOG(INFO) << "Device doesn't use avb, skipping setting vbmeta digest";
return;
}
if (reset) {
SetVbmetaDigestProp("");
return;
}
std::string digest = CalculateVbmetaDigestForInactiveSlot();
if (digest.empty()) {
LOG(WARNING) << "Failed to calculate the vbmeta digest for the other slot";
return;
}
SetVbmetaDigestProp(digest);
}
string HardwareAndroid::GetVersionForLogging(
const string& partition_name) const {
if (partition_name == "boot") {
// ro.bootimage.build.date.utc
return GetPartitionBuildDate("bootimage");
}
return GetPartitionBuildDate(partition_name);
}
ErrorCode HardwareAndroid::IsPartitionUpdateValid(
const string& partition_name, const string& new_version) const {
if (partition_name == "boot") {
const auto old_version = GetPartitionBuildDate("bootimage");
auto error_code =
IsTimestampNewerLogged(partition_name, old_version, new_version);
if (error_code == ErrorCode::kPayloadTimestampError) {
bool prevent_downgrade =
android::sysprop::GkiProperties::prevent_downgrade_version().value_or(
false);
if (!prevent_downgrade) {
LOG(WARNING) << "Downgrade of boot image is detected, but permitting "
"update because device does not prevent boot image "
"downgrade";
// If prevent_downgrade_version sysprop is not explicitly set, permit
// downgrade in boot image version.
// Even though error_code is overridden here, always call
// IsTimestampNewerLogged to produce log messages.
error_code = ErrorCode::kSuccess;
}
}
return error_code;
}
const auto old_version = GetPartitionBuildDate(partition_name);
// TODO(zhangkelvin) for some partitions, missing a current timestamp should
// be an error, e.g. system, vendor, product etc.
auto error_code =
IsTimestampNewerLogged(partition_name, old_version, new_version);
return error_code;
}
// Mount options for non-system partitions. This option causes selinux treat
// every file in the mounted filesystem as having the 'postinstall_file'
// context, regardless of what the filesystem itself records. See "SELinux
// User's and Administrator's Guide" for more information on this option.
constexpr const char* kDefaultPostinstallMountOptions =
"context=u:object_r:postinstall_file:s0";
// Mount options for system partitions. This option causes selinux to use the
// 'postinstall_file' context as a fallback if there are no other selinux
// contexts associated with the file in the mounted partition. See "SELinux
// User's and Administrator's Guide" for more information on this option.
constexpr const char* kSystemPostinstallMountOptions =
"defcontext=u:object_r:postinstall_file:s0";
// Name of the system-partition
constexpr std::string_view kSystemPartitionName = "system";
const char* HardwareAndroid::GetPartitionMountOptions(
const std::string& partition_name) const {
if (partition_name == kSystemPartitionName) {
return kSystemPostinstallMountOptions;
} else {
return kDefaultPostinstallMountOptions;
}
}
} // namespace chromeos_update_engine
|