Snap for 4696223 from 90336bc85e to qt-release
Change-Id: I3e29c7c71f63e62001a6af9de50acedb618eff16
This commit is contained in:
+143
@@ -0,0 +1,143 @@
|
||||
// Copyright (C) 2018 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.
|
||||
|
||||
cc_defaults {
|
||||
name: "recovery_defaults",
|
||||
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
],
|
||||
}
|
||||
|
||||
// Generic device that uses ScreenRecoveryUI.
|
||||
cc_library_static {
|
||||
name: "librecovery_ui_default",
|
||||
|
||||
defaults: [
|
||||
"recovery_defaults",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"default_device.cpp",
|
||||
],
|
||||
}
|
||||
|
||||
// The default wear device that uses WearRecoveryUI.
|
||||
cc_library_static {
|
||||
name: "librecovery_ui_wear",
|
||||
|
||||
defaults: [
|
||||
"recovery_defaults",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"wear_device.cpp",
|
||||
],
|
||||
}
|
||||
|
||||
// The default VR device that uses VrRecoveryUI.
|
||||
cc_library_static {
|
||||
name: "librecovery_ui_vr",
|
||||
|
||||
defaults: [
|
||||
"recovery_defaults",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"vr_device.cpp",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library_static {
|
||||
name: "libmounts",
|
||||
|
||||
defaults: [
|
||||
"recovery_defaults",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"mounts.cpp",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libbase",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library_static {
|
||||
name: "libverifier",
|
||||
|
||||
defaults: [
|
||||
"recovery_defaults",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"asn1_decoder.cpp",
|
||||
"verifier.cpp",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libbase",
|
||||
"libcrypto",
|
||||
"libcrypto_utils",
|
||||
"libotautil",
|
||||
],
|
||||
}
|
||||
|
||||
// The dynamic executable that runs after /data mounts.
|
||||
cc_binary {
|
||||
name: "recovery-persist",
|
||||
|
||||
defaults: [
|
||||
"recovery_defaults",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"recovery-persist.cpp",
|
||||
"rotate_logs.cpp",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"liblog",
|
||||
],
|
||||
|
||||
init_rc: [
|
||||
"recovery-persist.rc",
|
||||
],
|
||||
}
|
||||
|
||||
// The dynamic executable that runs at init.
|
||||
cc_binary {
|
||||
name: "recovery-refresh",
|
||||
|
||||
defaults: [
|
||||
"recovery_defaults",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"recovery-refresh.cpp",
|
||||
"rotate_logs.cpp",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"liblog",
|
||||
],
|
||||
|
||||
init_rc: [
|
||||
"recovery-refresh.rc",
|
||||
],
|
||||
}
|
||||
+2
-83
@@ -23,17 +23,6 @@ RECOVERY_FSTAB_VERSION := 2
|
||||
# librecovery_ui_default, which uses ScreenRecoveryUI.
|
||||
TARGET_RECOVERY_UI_LIB ?= librecovery_ui_default
|
||||
|
||||
# libmounts (static library)
|
||||
# ===============================
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := mounts.cpp
|
||||
LOCAL_CFLAGS := \
|
||||
-Wall \
|
||||
-Werror
|
||||
LOCAL_MODULE := libmounts
|
||||
LOCAL_STATIC_LIBRARIES := libbase
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
# librecovery (static library)
|
||||
# ===============================
|
||||
include $(CLEAR_VARS)
|
||||
@@ -49,6 +38,7 @@ ifeq ($(AB_OTA_UPDATER),true)
|
||||
endif
|
||||
|
||||
LOCAL_MODULE := librecovery
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libminui \
|
||||
libotautil \
|
||||
@@ -72,6 +62,7 @@ LOCAL_SRC_FILES := \
|
||||
LOCAL_CFLAGS := -Wall -Werror
|
||||
|
||||
LOCAL_MODULE := librecovery_ui
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libminui \
|
||||
libbase
|
||||
@@ -213,78 +204,6 @@ endif
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
# recovery-persist (system partition dynamic executable run after /data mounts)
|
||||
# ===============================
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := \
|
||||
recovery-persist.cpp \
|
||||
rotate_logs.cpp
|
||||
LOCAL_MODULE := recovery-persist
|
||||
LOCAL_SHARED_LIBRARIES := liblog libbase
|
||||
LOCAL_CFLAGS := -Wall -Werror
|
||||
LOCAL_INIT_RC := recovery-persist.rc
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
# recovery-refresh (system partition dynamic executable run at init)
|
||||
# ===============================
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := \
|
||||
recovery-refresh.cpp \
|
||||
rotate_logs.cpp
|
||||
LOCAL_MODULE := recovery-refresh
|
||||
LOCAL_SHARED_LIBRARIES := liblog libbase
|
||||
LOCAL_CFLAGS := -Wall -Werror
|
||||
LOCAL_INIT_RC := recovery-refresh.rc
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
# libverifier (static library)
|
||||
# ===============================
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libverifier
|
||||
LOCAL_SRC_FILES := \
|
||||
asn1_decoder.cpp \
|
||||
verifier.cpp
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
libotautil \
|
||||
libcrypto_utils \
|
||||
libcrypto \
|
||||
libbase
|
||||
LOCAL_CFLAGS := -Wall -Werror
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
# Generic device that uses ScreenRecoveryUI.
|
||||
# ===============================
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := default_device.cpp
|
||||
LOCAL_CFLAGS := -Wall -Werror
|
||||
|
||||
LOCAL_MODULE := librecovery_ui_default
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
# Wear default device
|
||||
# ===============================
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := wear_device.cpp
|
||||
LOCAL_CFLAGS := -Wall -Werror
|
||||
|
||||
# Should match TARGET_RECOVERY_UI_LIB in BoardConfig.mk.
|
||||
LOCAL_MODULE := librecovery_ui_wear
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
# vr headset default device
|
||||
# ===============================
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := vr_device.cpp
|
||||
LOCAL_CFLAGS := -Wall -Werror
|
||||
|
||||
# should match TARGET_RECOVERY_UI_LIB set in BoardConfig.mk
|
||||
LOCAL_MODULE := librecovery_ui_vr
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
include \
|
||||
$(LOCAL_PATH)/boot_control/Android.mk \
|
||||
$(LOCAL_PATH)/minui/Android.mk \
|
||||
|
||||
+2
-2
@@ -1557,14 +1557,14 @@ int main(int argc, char **argv) {
|
||||
// to log the update attempt since update_package is non-NULL.
|
||||
modified_flash = true;
|
||||
|
||||
if (!is_battery_ok()) {
|
||||
if (retry_count == 0 && !is_battery_ok()) {
|
||||
ui->Print("battery capacity is not enough for installing package, needed is %d%%\n",
|
||||
BATTERY_OK_PERCENTAGE);
|
||||
// Log the error code to last_install when installation skips due to
|
||||
// low battery.
|
||||
log_failure_code(kLowBattery, update_package);
|
||||
status = INSTALL_SKIPPED;
|
||||
} else if (bootreason_in_blacklist()) {
|
||||
} else if (retry_count == 0 && bootreason_in_blacklist()) {
|
||||
// Skip update-on-reboot when bootreason is kernel_panic or similar
|
||||
ui->Print("bootreason is in the blacklist; skip OTA installation\n");
|
||||
log_failure_code(kBootreasonInBlacklist, update_package);
|
||||
|
||||
@@ -22,4 +22,6 @@ LOCAL_PACKAGE_NAME := SystemUpdateApp
|
||||
LOCAL_SDK_VERSION := system_current
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||
|
||||
include $(BUILD_PACKAGE)
|
||||
|
||||
@@ -14,8 +14,18 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.android.update">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.update">
|
||||
|
||||
<application android:label="Sample Updater">
|
||||
<activity android:name=".ui.SystemUpdateActivity"
|
||||
android:label="Sample Updater">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<!--
|
||||
Copyright (C) 2018 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.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
package com.android.update.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.UpdateEngine;
|
||||
import android.os.UpdateEngineCallback;
|
||||
|
||||
/** Main update activity. */
|
||||
public class SystemUpdateActivity extends Activity {
|
||||
|
||||
private UpdateEngine updateEngine;
|
||||
private UpdateEngineCallbackImpl updateEngineCallbackImpl = new UpdateEngineCallbackImpl(this);
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
updateEngine = new UpdateEngine();
|
||||
updateEngine.bind(updateEngineCallbackImpl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
updateEngine.unbind();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
void onStatusUpdate(int i, float v) {
|
||||
// Handle update engine status update
|
||||
}
|
||||
|
||||
void onPayloadApplicationComplete(int i) {
|
||||
// Handle apply payload completion
|
||||
}
|
||||
|
||||
private static class UpdateEngineCallbackImpl extends UpdateEngineCallback {
|
||||
|
||||
private final SystemUpdateActivity activity;
|
||||
|
||||
public UpdateEngineCallbackImpl(SystemUpdateActivity activity) {
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusUpdate(int i, float v) {
|
||||
activity.onStatusUpdate(i, v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPayloadApplicationComplete(int i) {
|
||||
activity.onPayloadApplicationComplete(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
-42
@@ -45,6 +45,8 @@ LOCAL_SRC_FILES := \
|
||||
|
||||
LOCAL_C_INCLUDES := bootable/recovery
|
||||
LOCAL_SHARED_LIBRARIES := liblog
|
||||
LOCAL_TEST_DATA := \
|
||||
$(call find-test-data-in-subdirs, $(LOCAL_PATH), "*", testdata)
|
||||
include $(BUILD_NATIVE_TEST)
|
||||
|
||||
# Manual tests
|
||||
@@ -61,25 +63,8 @@ LOCAL_SHARED_LIBRARIES := \
|
||||
liblog \
|
||||
libpng
|
||||
|
||||
resource_files := $(call find-files-in-subdirs, bootable/recovery, \
|
||||
"*_text.png", \
|
||||
res-mdpi/images \
|
||||
res-hdpi/images \
|
||||
res-xhdpi/images \
|
||||
res-xxhdpi/images \
|
||||
res-xxxhdpi/images \
|
||||
)
|
||||
|
||||
# The resource image files that will go to $OUT/data/nativetest/recovery.
|
||||
testimage_out_path := $(TARGET_OUT_DATA)/nativetest/recovery
|
||||
GEN := $(addprefix $(testimage_out_path)/, $(resource_files))
|
||||
|
||||
$(GEN): PRIVATE_PATH := $(LOCAL_PATH)
|
||||
$(GEN): PRIVATE_CUSTOM_TOOL = cp $< $@
|
||||
$(GEN): $(testimage_out_path)/% : bootable/recovery/%
|
||||
$(transform-generated-source)
|
||||
LOCAL_GENERATED_SOURCES += $(GEN)
|
||||
|
||||
LOCAL_TEST_DATA := \
|
||||
$(call find-test-data-in-subdirs, bootable/recovery, "*_text.png", res-*)
|
||||
include $(BUILD_NATIVE_TEST)
|
||||
|
||||
# Component tests
|
||||
@@ -171,29 +156,8 @@ LOCAL_STATIC_LIBRARIES := \
|
||||
libBionicGtestMain \
|
||||
$(tune2fs_static_libraries)
|
||||
|
||||
testdata_files := $(call find-subdir-files, testdata/*)
|
||||
|
||||
# The testdata files that will go to $OUT/data/nativetest/recovery.
|
||||
testdata_out_path := $(TARGET_OUT_DATA)/nativetest/recovery
|
||||
GEN := $(addprefix $(testdata_out_path)/, $(testdata_files))
|
||||
$(GEN): PRIVATE_PATH := $(LOCAL_PATH)
|
||||
$(GEN): PRIVATE_CUSTOM_TOOL = cp $< $@
|
||||
$(GEN): $(testdata_out_path)/% : $(LOCAL_PATH)/%
|
||||
$(transform-generated-source)
|
||||
LOCAL_GENERATED_SOURCES += $(GEN)
|
||||
|
||||
# A copy of the testdata to be packed into continuous_native_tests.zip.
|
||||
testdata_continuous_zip_prefix := \
|
||||
$(call intermediates-dir-for,PACKAGING,recovery_component_test)/DATA
|
||||
testdata_continuous_zip_path := $(testdata_continuous_zip_prefix)/nativetest/recovery
|
||||
GEN := $(addprefix $(testdata_continuous_zip_path)/, $(testdata_files))
|
||||
$(GEN): PRIVATE_PATH := $(LOCAL_PATH)
|
||||
$(GEN): PRIVATE_CUSTOM_TOOL = cp $< $@
|
||||
$(GEN): $(testdata_continuous_zip_path)/% : $(LOCAL_PATH)/%
|
||||
$(transform-generated-source)
|
||||
LOCAL_GENERATED_SOURCES += $(GEN)
|
||||
LOCAL_PICKUP_FILES := $(testdata_continuous_zip_prefix)
|
||||
|
||||
LOCAL_TEST_DATA := \
|
||||
$(call find-test-data-in-subdirs, $(LOCAL_PATH), "*", testdata)
|
||||
include $(BUILD_NATIVE_TEST)
|
||||
|
||||
# Host tests
|
||||
@@ -222,4 +186,6 @@ LOCAL_STATIC_LIBRARIES := \
|
||||
libBionicGtestMain
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
liblog
|
||||
LOCAL_TEST_DATA := \
|
||||
$(call find-test-data-in-subdirs, $(LOCAL_PATH), "*", testdata)
|
||||
include $(BUILD_HOST_NATIVE_TEST)
|
||||
|
||||
@@ -16,16 +16,18 @@
|
||||
<configuration description="Config for recovery_component_test and recovery_unit_test">
|
||||
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
|
||||
<option name="cleanup" value="true" />
|
||||
<option name="push" value="recovery_component_test->/data/local/tmp/recovery_component_test" />
|
||||
<option name="push" value="recovery_unit_test->/data/local/tmp/recovery_unit_test" />
|
||||
<option name="push" value="recovery_component_test->/data/local/tmp/recovery_component_test/recovery_component_test" />
|
||||
<option name="push" value="testdata->/data/local/tmp/recovery_component_test/testdata" />
|
||||
<option name="push" value="recovery_unit_test->/data/local/tmp/recovery_unit_test/recovery_unit_test" />
|
||||
<option name="push" value="testdata->/data/local/tmp/recovery_unit_test/testdata" />
|
||||
</target_preparer>
|
||||
<option name="test-suite-tag" value="apct" />
|
||||
<test class="com.android.tradefed.testtype.GTest" >
|
||||
<option name="native-test-device-path" value="/data/local/tmp" />
|
||||
<option name="native-test-device-path" value="/data/local/tmp/recovery_component_test" />
|
||||
<option name="module-name" value="recovery_component_test" />
|
||||
</test>
|
||||
<test class="com.android.tradefed.testtype.GTest" >
|
||||
<option name="native-test-device-path" value="/data/local/tmp" />
|
||||
<option name="native-test-device-path" value="/data/local/tmp/recovery_unit_test" />
|
||||
<option name="module-name" value="recovery_unit_test" />
|
||||
</test>
|
||||
</configuration>
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
#ifndef _OTA_TEST_CONSTANTS_H
|
||||
#define _OTA_TEST_CONSTANTS_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/file.h>
|
||||
|
||||
// Zip entries in ziptest_valid.zip.
|
||||
static const std::string kATxtContents("abcdefghabcdefgh\n");
|
||||
static const std::string kBTxtContents("abcdefgh\n");
|
||||
@@ -32,14 +32,9 @@ static const std::string kATxtSha1Sum("32c96a03dc8cd20097940f351bca6261ee5a1643"
|
||||
// echo -n -e "abcdefgh\n" | sha1sum
|
||||
static const std::string kBTxtSha1Sum("e414af7161c9554089f4106d6f1797ef14a73666");
|
||||
|
||||
static std::string from_testdata_base(const std::string& fname) {
|
||||
#ifdef __ANDROID__
|
||||
static std::string data_root = getenv("ANDROID_DATA");
|
||||
#else
|
||||
static std::string data_root = std::string(getenv("ANDROID_PRODUCT_OUT")) + "/data";
|
||||
#endif
|
||||
|
||||
return data_root + "/nativetest/recovery/testdata/" + fname;
|
||||
[[maybe_unused]] static std::string from_testdata_base(const std::string& fname) {
|
||||
static std::string exec_dir = android::base::GetExecutableDirectory();
|
||||
return exec_dir + "/testdata/" + fname;
|
||||
}
|
||||
|
||||
#endif // _OTA_TEST_CONSTANTS_H
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
static const std::string myFilename = "/data/misc/recovery/inject.txt";
|
||||
static const std::string myContent = "Hello World\nWelcome to my recovery\n";
|
||||
static const std::string kLocale = "zu";
|
||||
static const std::string kResourceTestDir = "/data/nativetest/recovery/";
|
||||
|
||||
// Failure is expected on systems that do not deliver either the
|
||||
// recovery-persist or recovery-refresh executables. Tests also require
|
||||
@@ -108,19 +107,20 @@ static int png_filter(const dirent* de) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Find out all png files to test under /data/nativetest/recovery/.
|
||||
// Find out all the PNG files to test, which stay under the same dir with the executable.
|
||||
static std::vector<std::string> add_files() {
|
||||
std::string exec_dir = android::base::GetExecutableDirectory();
|
||||
std::vector<std::string> files;
|
||||
for (const std::string& str : image_dir) {
|
||||
std::string dir_path = kResourceTestDir + str;
|
||||
for (const std::string& image : image_dir) {
|
||||
std::string dir_path = exec_dir + "/" + image;
|
||||
dirent** namelist;
|
||||
int n = scandir(dir_path.c_str(), &namelist, png_filter, alphasort);
|
||||
if (n == -1) {
|
||||
printf("Failed to scan dir %s: %s\n", kResourceTestDir.c_str(), strerror(errno));
|
||||
printf("Failed to scan dir %s: %s\n", exec_dir.c_str(), strerror(errno));
|
||||
return files;
|
||||
}
|
||||
if (n == 0) {
|
||||
printf("No file is added for test in %s\n", kResourceTestDir.c_str());
|
||||
printf("No file is added for test in %s\n", exec_dir.c_str());
|
||||
}
|
||||
|
||||
while (n--) {
|
||||
|
||||
Reference in New Issue
Block a user