Snippets Collections
SELECT(room_type[room id],[hotel id]=[_THISROW].[hotel name])
room_type[room id]-SELECT(Service Record[room],AND([checkin]<[_THISROW].[checkout],[checkout]>[_THISROW].[checkin],[No]<>[_THISROW].[No]))
bool checkPalindrome (string s, int start, int length) {
  if (start>length) {
    return true;
	}
  if (s[start] != s[length]) {
    return false;
	}
  else {
    return checkPalindrome(s,start+1,length-1;)
  }
}
blob: e522f4d4d3ff87ccb606a8549db4efd0e6228faa [file] [log] [blame]
/*
 * 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.
 */
#include "commands.h"
#include <inttypes.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unordered_set>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <android/hardware/boot/1.1/IBootControl.h>
#include <cutils/android_reboot.h>
#include <ext4_utils/wipe.h>
#include <fs_mgr.h>
#include <fs_mgr/roots.h>
#include <libgsi/libgsi.h>
#include <liblp/builder.h>
#include <liblp/liblp.h>
#include <libsnapshot/snapshot.h>
#include <storage_literals/storage_literals.h>
#include <uuid/uuid.h>
#include <bootloader_message/bootloader_message.h>
#include "BootControlClient.h"
#include "constants.h"
#include "fastboot_device.h"
#include "flashing.h"
#include "utility.h"
#ifdef FB_ENABLE_FETCH
static constexpr bool kEnableFetch = true;
#else
static constexpr bool kEnableFetch = false;
#endif
using android::fs_mgr::MetadataBuilder;
using android::hal::CommandResult;
using ::android::hardware::hidl_string;
using android::snapshot::SnapshotManager;
using MergeStatus = android::hal::BootControlClient::MergeStatus;
using namespace android::storage_literals;
struct VariableHandlers {
    // Callback to retrieve the value of a single variable.
    std::function<bool(FastbootDevice*, const std::vector<std::string>&, std::string*)> get;
    // Callback to retrieve all possible argument combinations, for getvar all.
    std::function<std::vector<std::vector<std::string>>(FastbootDevice*)> get_all_args;
};
static bool IsSnapshotUpdateInProgress(FastbootDevice* device) {
    auto hal = device->boot1_1();
    if (!hal) {
        return false;
    }
    auto merge_status = hal->getSnapshotMergeStatus();
    return merge_status == MergeStatus::SNAPSHOTTED || merge_status == MergeStatus::MERGING;
}
static bool IsProtectedPartitionDuringMerge(FastbootDevice* device, const std::string& name) {
    static const std::unordered_set<std::string> ProtectedPartitionsDuringMerge = {
            "userdata", "metadata", "misc"};
    if (ProtectedPartitionsDuringMerge.count(name) == 0) {
        return false;
    }
    return IsSnapshotUpdateInProgress(device);
}
static void GetAllVars(FastbootDevice* device, const std::string& name,
                       const VariableHandlers& handlers) {
    if (!handlers.get_all_args) {
        std::string message;
        if (!handlers.get(device, std::vector<std::string>(), &message)) {
            return;
        }
        device->WriteInfo(android::base::StringPrintf("%s:%s", name.c_str(), message.c_str()));
        return;
    }
    auto all_args = handlers.get_all_args(device);
    for (const auto& args : all_args) {
        std::string message;
        if (!handlers.get(device, args, &message)) {
            continue;
        }
        std::string arg_string = android::base::Join(args, ":");
        device->WriteInfo(android::base::StringPrintf("%s:%s:%s", name.c_str(), arg_string.c_str(),
                                                      message.c_str()));
    }
}
const std::unordered_map<std::string, VariableHandlers> kVariableMap = {
        {FB_VAR_VERSION, {GetVersion, nullptr}},
        {FB_VAR_VERSION_BOOTLOADER, {GetBootloaderVersion, nullptr}},
        {FB_VAR_VERSION_BASEBAND, {GetBasebandVersion, nullptr}},
        {FB_VAR_VERSION_OS, {GetOsVersion, nullptr}},
        {FB_VAR_VERSION_VNDK, {GetVndkVersion, nullptr}},
        {FB_VAR_PRODUCT, {GetProduct, nullptr}},
        {FB_VAR_SERIALNO, {GetSerial, nullptr}},
        {FB_VAR_VARIANT, {GetVariant, nullptr}},
        {FB_VAR_SECURE, {GetSecure, nullptr}},
        {FB_VAR_UNLOCKED, {GetUnlocked, nullptr}},
        {FB_VAR_MAX_DOWNLOAD_SIZE, {GetMaxDownloadSize, nullptr}},
        {FB_VAR_CURRENT_SLOT, {::GetCurrentSlot, nullptr}},
        {FB_VAR_SLOT_COUNT, {GetSlotCount, nullptr}},
        {FB_VAR_HAS_SLOT, {GetHasSlot, GetAllPartitionArgsNoSlot}},
        {FB_VAR_SLOT_SUCCESSFUL, {GetSlotSuccessful, nullptr}},
        {FB_VAR_SLOT_UNBOOTABLE, {GetSlotUnbootable, nullptr}},
        {FB_VAR_PARTITION_SIZE, {GetPartitionSize, GetAllPartitionArgsWithSlot}},
        {FB_VAR_PARTITION_TYPE, {GetPartitionType, GetAllPartitionArgsWithSlot}},
        {FB_VAR_IS_LOGICAL, {GetPartitionIsLogical, GetAllPartitionArgsWithSlot}},
        {FB_VAR_IS_USERSPACE, {GetIsUserspace, nullptr}},
        {FB_VAR_IS_FORCE_DEBUGGABLE, {GetIsForceDebuggable, nullptr}},
        {FB_VAR_OFF_MODE_CHARGE_STATE, {GetOffModeChargeState, nullptr}},
        {FB_VAR_BATTERY_VOLTAGE, {GetBatteryVoltage, nullptr}},
        {FB_VAR_BATTERY_SOC, {GetBatterySoC, nullptr}},
        {FB_VAR_BATTERY_SOC_OK, {GetBatterySoCOk, nullptr}},
        {FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}},
        {FB_VAR_SUPER_PARTITION_NAME, {GetSuperPartitionName, nullptr}},
        {FB_VAR_SNAPSHOT_UPDATE_STATUS, {GetSnapshotUpdateStatus, nullptr}},
        {FB_VAR_CPU_ABI, {GetCpuAbi, nullptr}},
        {FB_VAR_SYSTEM_FINGERPRINT, {GetSystemFingerprint, nullptr}},
        {FB_VAR_VENDOR_FINGERPRINT, {GetVendorFingerprint, nullptr}},
        {FB_VAR_DYNAMIC_PARTITION, {GetDynamicPartition, nullptr}},
        {FB_VAR_FIRST_API_LEVEL, {GetFirstApiLevel, nullptr}},
        {FB_VAR_SECURITY_PATCH_LEVEL, {GetSecurityPatchLevel, nullptr}},
        {FB_VAR_TREBLE_ENABLED, {GetTrebleEnabled, nullptr}},
        {FB_VAR_MAX_FETCH_SIZE, {GetMaxFetchSize, nullptr}},
        {FB_VAR_BATTERY_SERIAL_NUMBER, {GetBatterySerialNumber, nullptr}},
        {FB_VAR_BATTERY_PART_STATUS, {GetBatteryPartStatus, nullptr}},
};
static bool GetVarAll(FastbootDevice* device) {
    for (const auto& [name, handlers] : kVariableMap) {
        GetAllVars(device, name, handlers);
    }
    return true;
}
static void PostWipeData() {
    std::string err;
    // Reset mte state of device.
    if (!WriteMiscMemtagMessage({}, &err)) {
        LOG(ERROR) << "Failed to reset MTE state: " << err;
    }
}
const std::unordered_map<std::string, std::function<bool(FastbootDevice*)>> kSpecialVars = {
        {"all", GetVarAll},
        {"dmesg", GetDmesg},
};
bool GetVarHandler(FastbootDevice* device, const std::vector<std::string>& args) {
    if (args.size() < 2) {
        return device->WriteFail("Missing argument");
    }
    // "all" and "dmesg" are multiline and handled specially.
    auto found_special = kSpecialVars.find(args[1]);
    if (found_special != kSpecialVars.end()) {
        if (!found_special->second(device)) {
            return false;
        }
        return device->WriteOkay("");
    }
    // args[0] is command name, args[1] is variable.
    auto found_variable = kVariableMap.find(args[1]);
    if (found_variable == kVariableMap.end()) {
        return device->WriteFail("Unknown variable");
    }
    std::string message;
    std::vector<std::string> getvar_args(args.begin() + 2, args.end());
    if (!found_variable->second.get(device, getvar_args, &message)) {
        return device->WriteFail(message);
    }
    return device->WriteOkay(message);
}
bool OemPostWipeData(FastbootDevice* device) {
    auto fastboot_hal = device->fastboot_hal();
    if (!fastboot_hal) {
        return false;
    }
    auto status = fastboot_hal->doOemSpecificErase();
    if (status.isOk()) {
        device->WriteStatus(FastbootResult::OKAY, "Erasing succeeded");
        return true;
    }
    switch (status.getExceptionCode()) {
        case EX_UNSUPPORTED_OPERATION:
            return false;
        case EX_SERVICE_SPECIFIC:
            device->WriteStatus(FastbootResult::FAIL, status.getDescription());
            return false;
        default:
            LOG(ERROR) << "Erase operation failed" << status.getDescription();
            return false;
    }
}
bool EraseHandler(FastbootDevice* device, const std::vector<std::string>& args) {
    if (args.size() < 2) {
        return device->WriteStatus(FastbootResult::FAIL, "Invalid arguments");
    }
    if (GetDeviceLockStatus()) {
        return device->WriteStatus(FastbootResult::FAIL, "Erase is not allowed on locked devices");
    }
    const auto& partition_name = args[1];
    if (IsProtectedPartitionDuringMerge(device, partition_name)) {
        auto message = "Cannot erase " + partition_name + " while a snapshot update is in progress";
        return device->WriteFail(message);
    }
    PartitionHandle handle;
    if (!OpenPartition(device, partition_name, &handle)) {
        return device->WriteStatus(FastbootResult::FAIL, "Partition doesn't exist");
    }
    if (wipe_block_device(handle.fd(), get_block_device_size(handle.fd())) == 0) {
        //Perform oem PostWipeData if Android userdata partition has been erased
        bool support_oem_postwipedata = false;
        if (partition_name == "userdata") {
            PostWipeData();
            support_oem_postwipedata = OemPostWipeData(device);
        }
        if (!support_oem_postwipedata) {
            return device->WriteStatus(FastbootResult::OKAY, "Erasing succeeded");
        } else {
            //Write device status in OemPostWipeData(), so just return true
            return true;
        }
    }
    return device->WriteStatus(FastbootResult::FAIL, "Erasing failed");
}
bool OemCmdHandler(FastbootDevice* device, const std::vector<std::string>& args) {
    auto fastboot_hal = device->fastboot_hal();
    if (!fastboot_hal) {
        return device->WriteStatus(FastbootResult::FAIL, "Unable to open fastboot HAL");
    }
    //Disable "oem postwipedata userdata" to prevent user wipe oem userdata only.
    if (args[0] == "oem postwipedata userdata") {
        return device->WriteStatus(FastbootResult::FAIL, "Unable to do oem postwipedata userdata");
    }
    std::string message;
    auto status = fastboot_hal->doOemCommand(args[0], &message);
    if (!status.isOk()) {
        LOG(ERROR) << "Unable to do OEM command " << args[0].c_str() << status.getDescription();
        return device->WriteStatus(FastbootResult::FAIL,
                                   "Unable to do OEM command " + status.getDescription());
    }
    device->WriteInfo(message);
    return device->WriteStatus(FastbootResult::OKAY, message);
}
bool DownloadHandler(FastbootDevice* device, const std::vector<std::string>& args) {
    if (args.size() < 2) {
        return device->WriteStatus(FastbootResult::FAIL, "size argument unspecified");
    }
    if (GetDeviceLockStatus()) {
        return device->WriteStatus(FastbootResult::FAIL,
                                   "Download is not allowed on locked devices");
    }
    // arg[0] is the command name, arg[1] contains size of data to be downloaded
    // which should always be 8 bytes
    if (args[1].length() != 8) {
        return device->WriteStatus(FastbootResult::FAIL,
                                   "Invalid size (length of size != 8)");
    }
    unsigned int size;
    if (!android::base::ParseUint("0x" + args[1], &size, kMaxDownloadSizeDefault)) {
        return device->WriteStatus(FastbootResult::FAIL, "Invalid size");
    }
    if (size == 0) {
        return device->WriteStatus(FastbootResult::FAIL, "Invalid size (0)");
    }
    device->download_data().resize(size);
    if (!device->WriteStatus(FastbootResult::DATA, android::base::StringPrintf("%08x", size))) {
        return false;
    }
    if (device->HandleData(true, &device->download_data())) {
        return device->WriteStatus(FastbootResult::OKAY, "");
    }
    PLOG(ERROR) << "Couldn't download data";
    return device->WriteStatus(FastbootResult::FAIL, "Couldn't download data");
}
bool SetActiveHandler(FastbootDevice* device, const std::vector<std::string>& args) {
    if (args.size() < 2) {
        return device->WriteStatus(FastbootResult::FAIL, "Missing slot argument");
    }
    if (GetDeviceLockStatus()) {
        return device->WriteStatus(FastbootResult::FAIL,
                                   "set_active command is not allowed on locked devices");
    }
    int32_t slot = 0;
    if (!GetSlotNumber(args[1], &slot)) {
        // Slot suffix needs to be between 'a' and 'z'.
        return device->WriteStatus(FastbootResult::FAIL, "Bad slot suffix");
    }
    // Non-A/B devices will not have a boot control HAL.
    auto boot_control_hal = device->boot_control_hal();
    if (!boot_control_hal) {
        return device->WriteStatus(FastbootResult::FAIL,
                                   "Cannot set slot: boot control HAL absent");
    }
    if (slot >= boot_control_hal->GetNumSlots()) {
        return device->WriteStatus(FastbootResult::FAIL, "Slot out of range");
    }
    // If the slot is not changing, do nothing.
    if (args[1] == device->GetCurrentSlot()) {
        return device->WriteOkay("");
    }
    // Check how to handle the current snapshot state.
    if (auto hal11 = device->boot1_1()) {
        auto merge_status = hal11->getSnapshotMergeStatus();
        if (merge_status == MergeStatus::MERGING) {
            return device->WriteFail("Cannot change slots while a snapshot update is in progress");
        }
        // Note: we allow the slot change if the state is SNAPSHOTTED. First-
        // stage init does not have access to the HAL, and uses the slot number
        // and /metadata OTA state to determine whether a slot change occurred.
        // Booting into the old slot would erase the OTA, and switching A->B->A
        // would simply resume it if no boots occur in between. Re-flashing
        // partitions implicitly cancels the OTA, so leaving the state as-is is
        // safe.
        if (merge_status == MergeStatus::SNAPSHOTTED) {
            device->WriteInfo(
                    "Changing the active slot with a snapshot applied may cancel the"
                    " update.");
        }
    }
    CommandResult ret = boot_control_hal->SetActiveBootSlot(slot);
    if (ret.success) {
        // Save as slot suffix to match the suffix format as returned from
        // the boot control HAL.
        auto current_slot = "_" + args[1];
        device->set_active_slot(current_slot);
        return device->WriteStatus(FastbootResult::OKAY, "");
    }
    return device->WriteStatus(FastbootResult::FAIL, "Unable to set slot");
}
bool ShutDownHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {
    auto result = device->WriteStatus(FastbootResult::OKAY, "Shutting down");
    android::base::SetProperty(ANDROID_RB_PROPERTY, "shutdown,fastboot");
    device->CloseDevice();
    TEMP_FAILURE_RETRY(pause());
    return result;
}
bool RebootHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {
    auto result = device->WriteStatus(FastbootResult::OKAY, "Rebooting");
    android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,from_fastboot");
    device->CloseDevice();
    TEMP_FAILURE_RETRY(pause());
    return result;
}
bool RebootBootloaderHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {
    auto result = device->WriteStatus(FastbootResult::OKAY, "Rebooting bootloader");
    android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,bootloader");
    device->CloseDevice();
    TEMP_FAILURE_RETRY(pause());
    return result;
}
bool RebootFastbootHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {
    auto result = device->WriteStatus(FastbootResult::OKAY, "Rebooting fastboot");
    android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,fastboot");
    device->CloseDevice();
    TEMP_FAILURE_RETRY(pause());
    return result;
}
static bool EnterRecovery() {
    const char msg_switch_to_recovery = 'r';
    android::base::unique_fd sock(socket(AF_UNIX, SOCK_STREAM, 0));
    if (sock < 0) {
        PLOG(ERROR) << "Couldn't create sock";
        return false;
    }
    struct sockaddr_un addr = {.sun_family = AF_UNIX};
    strncpy(addr.sun_path, "/dev/socket/recovery", sizeof(addr.sun_path) - 1);
    if (connect(sock.get(), (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        PLOG(ERROR) << "Couldn't connect to recovery";
        return false;
    }
    // Switch to recovery will not update the boot reason since it does not
    // require a reboot.
    auto ret = write(sock.get(), &msg_switch_to_recovery, sizeof(msg_switch_to_recovery));
    if (ret != sizeof(msg_switch_to_recovery)) {
        PLOG(ERROR) << "Couldn't write message to switch to recovery";
        return false;
    }
    return true;
}
bool RebootRecoveryHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {
    auto status = true;
    if (EnterRecovery()) {
        status = device->WriteStatus(FastbootResult::OKAY, "Rebooting to recovery");
    } else {
        status = device->WriteStatus(FastbootResult::FAIL, "Unable to reboot to recovery");
    }
    device->CloseDevice();
    TEMP_FAILURE_RETRY(pause());
    return status;
}
// Helper class for opening a handle to a MetadataBuilder and writing the new
// partition table to the same place it was read.
class PartitionBuilder {
  public:
    explicit PartitionBuilder(FastbootDevice* device, const std::string& partition_name);
    bool Write();
    bool Valid() const { return !!builder_; }
    MetadataBuilder* operator->() const { return builder_.get(); }
  private:
    FastbootDevice* device_;
    std::string super_device_;
    uint32_t slot_number_;
    std::unique_ptr<MetadataBuilder> builder_;
};
PartitionBuilder::PartitionBuilder(FastbootDevice* device, const std::string& partition_name)
    : device_(device) {
    std::string slot_suffix = GetSuperSlotSuffix(device, partition_name);
    slot_number_ = android::fs_mgr::SlotNumberForSlotSuffix(slot_suffix);
    auto super_device = FindPhysicalPartition(fs_mgr_get_super_partition_name(slot_number_));
    if (!super_device) {
        return;
    }
    super_device_ = *super_device;
    builder_ = MetadataBuilder::New(super_device_, slot_number_);
}
bool PartitionBuilder::Write() {
    auto metadata = builder_->Export();
    if (!metadata) {
        return false;
    }
    return UpdateAllPartitionMetadata(device_, super_device_, *metadata.get());
}
bool CreatePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args) {
    if (args.size() < 3) {
        return device->WriteFail("Invalid partition name and size");
    }
    if (GetDeviceLockStatus()) {
        return device->WriteStatus(FastbootResult::FAIL, "Command not available on locked devices");
    }
    uint64_t partition_size;
    std::string partition_name = args[1];
    if (!android::base::ParseUint(args[2].c_str(), &partition_size)) {
        return device->WriteFail("Invalid partition size");
    }
    PartitionBuilder builder(device, partition_name);
    if (!builder.Valid()) {
        return device->WriteFail("Could not open super partition");
    }
    // TODO(112433293) Disallow if the name is in the physical table as well.
    if (builder->FindPartition(partition_name)) {
        return device->WriteFail("Partition already exists");
    }
    auto partition = builder->AddPartition(partition_name, 0);
    if (!partition) {
        return device->WriteFail("Failed to add partition");
    }
    if (!builder->ResizePartition(partition, partition_size)) {
        builder->RemovePartition(partition_name);
        return device->WriteFail("Not enough space for partition");
    }
    if (!builder.Write()) {
        return device->WriteFail("Failed to write partition table");
    }
    return device->WriteOkay("Partition created");
}
bool DeletePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args) {
    if (args.size() < 2) {
        return device->WriteFail("Invalid partition name and size");
    }
    if (GetDeviceLockStatus()) {
        return device->WriteStatus(FastbootResult::FAIL, "Command not available on locked devices");
    }
    std::string partition_name = args[1];
    PartitionBuilder builder(device, partition_name);
    if (!builder.Valid()) {
        return device->WriteFail("Could not open super partition");
    }
    builder->RemovePartition(partition_name);
    if (!builder.Write()) {
        return device->WriteFail("Failed to write partition table");
    }
    return device->WriteOkay("Partition deleted");
}
bool ResizePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args) {
    if (args.size() < 3) {
        return device->WriteFail("Invalid partition name and size");
    }
    if (GetDeviceLockStatus()) {
        return device->WriteStatus(FastbootResult::FAIL, "Command not available on locked devices");
    }
    uint64_t partition_size;
    std::string partition_name = args[1];
    if (!android::base::ParseUint(args[2].c_str(), &partition_size)) {
        return device->WriteFail("Invalid partition size");
    }
    PartitionBuilder builder(device, partition_name);
    if (!builder.Valid()) {
        return device->WriteFail("Could not open super partition");
    }
    auto partition = builder->FindPartition(partition_name);
    if (!partition) {
        return device->WriteFail("Partition does not exist");
    }
    // Remove the updated flag to cancel any snapshots.
    uint32_t attrs = partition->attributes();
    partition->set_attributes(attrs & ~LP_PARTITION_ATTR_UPDATED);
    if (!builder->ResizePartition(partition, partition_size)) {
        return device->WriteFail("Not enough space to resize partition");
    }
    if (!builder.Write()) {
        return device->WriteFail("Failed to write partition table");
    }
    return device->WriteOkay("Partition resized");
}
void CancelPartitionSnapshot(FastbootDevice* device, const std::string& partition_name) {
    PartitionBuilder builder(device, partition_name);
    if (!builder.Valid()) return;
    auto partition = builder->FindPartition(partition_name);
    if (!partition) return;
    // Remove the updated flag to cancel any snapshots.
    uint32_t attrs = partition->attributes();
    partition->set_attributes(attrs & ~LP_PARTITION_ATTR_UPDATED);
    builder.Write();
}
bool FlashHandler(FastbootDevice* device, const std::vector<std::string>& args) {
    if (args.size() < 2) {
        return device->WriteStatus(FastbootResult::FAIL, "Invalid arguments");
    }
    if (GetDeviceLockStatus()) {
        return device->WriteStatus(FastbootResult::FAIL,
                                   "Flashing is not allowed on locked devices");
    }
    const auto& partition_name = args[1];
    if (IsProtectedPartitionDuringMerge(device, partition_name)) {
        auto message = "Cannot flash " + partition_name + " while a snapshot update is in progress";
        return device->WriteFail(message);
    }
    if (LogicalPartitionExists(device, partition_name)) {
        CancelPartitionSnapshot(device, partition_name);
    }
    int ret = Flash(device, partition_name);
    if (ret < 0) {
        return device->WriteStatus(FastbootResult::FAIL, strerror(-ret));
    }
    if (partition_name == "userdata") {
        PostWipeData();
    }
    return device->WriteStatus(FastbootResult::OKAY, "Flashing succeeded");
}
bool UpdateSuperHandler(FastbootDevice* device, const std::vector<std::string>& args) {
    if (args.size() < 2) {
        return device->WriteFail("Invalid arguments");
    }
    if (GetDeviceLockStatus()) {
        return device->WriteStatus(FastbootResult::FAIL, "Command not available on locked devices");
    }
    bool wipe = (args.size() >= 3 && args[2] == "wipe");
    return UpdateSuper(device, args[1], wipe);
}
static bool IsLockedDsu() {
    std::string active_dsu;
    android::gsi::GetActiveDsu(&active_dsu);
    return android::base::EndsWith(active_dsu, ".lock");
}
bool GsiHandler(FastbootDevice* device, const std::vector<std::string>& args) {
    if (args.size() != 2) {
        return device->WriteFail("Invalid arguments");
    }
    AutoMountMetadata mount_metadata;
    if (!mount_metadata) {
        return device->WriteFail("Could not find GSI install");
    }
    if (!android::gsi::IsGsiInstalled()) {
        return device->WriteStatus(FastbootResult::FAIL, "No GSI is installed");
    }
    if ((args[1] == "wipe" || args[1] == "disable") && GetDeviceLockStatus() && IsLockedDsu()) {
        // Block commands that modify the states of locked DSU
        return device->WriteFail("Command not available on locked DSU/devices");
    }
    if (args[1] == "wipe") {
        if (!android::gsi::UninstallGsi()) {
            return device->WriteStatus(FastbootResult::FAIL, strerror(errno));
        }
    } else if (args[1] == "disable") {
        if (!android::gsi::DisableGsi()) {
            return device->WriteStatus(FastbootResult::FAIL, strerror(errno));
        }
    } else if (args[1] == "status") {
        std::string active_dsu;
        if (!android::gsi::IsGsiRunning()) {
            device->WriteInfo("Not running");
        } else if (!android::gsi::GetActiveDsu(&active_dsu)) {
            return device->WriteFail(strerror(errno));
        } else {
            device->WriteInfo("Running active DSU: " + active_dsu);
        }
    } else {
        return device->WriteFail("Invalid arguments");
    }
    return device->WriteStatus(FastbootResult::OKAY, "Success");
}
bool SnapshotUpdateHandler(FastbootDevice* device, const std::vector<std::string>& args) {
    // Note that we use the HAL rather than mounting /metadata, since we want
    // our results to match the bootloader.
    auto hal = device->boot1_1();
    if (!hal) return device->WriteFail("Not supported");
    // If no arguments, return the same thing as a getvar. Note that we get the
    // HAL first so we can return "not supported" before we return the less
    // specific error message below.
    if (args.size() < 2 || args[1].empty()) {
        std::string message;
        if (!GetSnapshotUpdateStatus(device, {}, &message)) {
            return device->WriteFail("Could not determine update status");
        }
        device->WriteInfo(message);
        return device->WriteOkay("");
    }
    MergeStatus status = hal->getSnapshotMergeStatus();
    if (args.size() != 2) {
        return device->WriteFail("Invalid arguments");
    }
    if (args[1] == "cancel") {
        switch (status) {
            case MergeStatus::SNAPSHOTTED:
            case MergeStatus::MERGING: {
                const auto ret = hal->SetSnapshotMergeStatus(MergeStatus::CANCELLED);
                if (!ret.success) {
                    device->WriteFail("Failed to SetSnapshotMergeStatus(MergeStatus::CANCELLED) " +
                                      ret.errMsg);
                }
                break;
            }
            default:
                break;
        }
    } else if (args[1] == "merge") {
        if (status != MergeStatus::MERGING) {
            return device->WriteFail("No snapshot merge is in progress");
        }
        auto sm = SnapshotManager::New();
        if (!sm) {
            return device->WriteFail("Unable to create SnapshotManager");
        }
        if (!sm->FinishMergeInRecovery()) {
            return device->WriteFail("Unable to finish snapshot merge");
        }
    } else {
        return device->WriteFail("Invalid parameter to snapshot-update");
    }
    return device->WriteStatus(FastbootResult::OKAY, "Success");
}
namespace {
// Helper of FetchHandler.
class PartitionFetcher {
  public:
    static bool Fetch(FastbootDevice* device, const std::vector<std::string>& args) {
        if constexpr (!kEnableFetch) {
            return device->WriteFail("Fetch is not allowed on user build");
        }
        if (GetDeviceLockStatus()) {
            return device->WriteFail("Fetch is not allowed on locked devices");
        }
        PartitionFetcher fetcher(device, args);
        if (fetcher.Open()) {
            fetcher.Fetch();
        }
        CHECK(fetcher.ret_.has_value());
        return *fetcher.ret_;
    }
  private:
    PartitionFetcher(FastbootDevice* device, const std::vector<std::string>& args)
        : device_(device), args_(&args) {}
    // Return whether the partition is successfully opened.
    // If successfully opened, ret_ is left untouched. Otherwise, ret_ is set to the value
    // that FetchHandler should return.
    bool Open() {
        if (args_->size() < 2) {
            ret_ = device_->WriteFail("Missing partition arg");
            return false;
        }
        partition_name_ = args_->at(1);
        if (std::find(kAllowedPartitions.begin(), kAllowedPartitions.end(), partition_name_) ==
            kAllowedPartitions.end()) {
            ret_ = device_->WriteFail("Fetch is only allowed on [" +
                                      android::base::Join(kAllowedPartitions, ", ") + "]");
            return false;
        }
        if (!OpenPartition(device_, partition_name_, &handle_, O_RDONLY)) {
            ret_ = device_->WriteFail(
                    android::base::StringPrintf("Cannot open %s", partition_name_.c_str()));
            return false;
        }
        partition_size_ = get_block_device_size(handle_.fd());
        if (partition_size_ == 0) {
            ret_ = device_->WriteOkay(android::base::StringPrintf("Partition %s has size 0",
                                                                  partition_name_.c_str()));
            return false;
        }
        start_offset_ = 0;
        if (args_->size() >= 3) {
            if (!android::base::ParseUint(args_->at(2), &start_offset_)) {
                ret_ = device_->WriteFail("Invalid offset, must be integer");
                return false;
            }
            if (start_offset_ > std::numeric_limits<off64_t>::max()) {
                ret_ = device_->WriteFail(
                        android::base::StringPrintf("Offset overflows: %" PRIx64, start_offset_));
                return false;
            }
        }
        if (start_offset_ > partition_size_) {
            ret_ = device_->WriteFail(android::base::StringPrintf(
                    "Invalid offset 0x%" PRIx64 ", partition %s has size 0x%" PRIx64, start_offset_,
                    partition_name_.c_str(), partition_size_));
            return false;
        }
        uint64_t maximum_total_size_to_read = partition_size_ - start_offset_;
        total_size_to_read_ = maximum_total_size_to_read;
        if (args_->size() >= 4) {
            if (!android::base::ParseUint(args_->at(3), &total_size_to_read_)) {
                ret_ = device_->WriteStatus(FastbootResult::FAIL, "Invalid size, must be integer");
                return false;
            }
        }
        if (total_size_to_read_ == 0) {
            ret_ = device_->WriteOkay("Read 0 bytes");
            return false;
        }
        if (total_size_to_read_ > maximum_total_size_to_read) {
            ret_ = device_->WriteFail(android::base::StringPrintf(
                    "Invalid size to read 0x%" PRIx64 ", partition %s has size 0x%" PRIx64
                    " and fetching from offset 0x%" PRIx64,
                    total_size_to_read_, partition_name_.c_str(), partition_size_, start_offset_));
            return false;
        }
        if (total_size_to_read_ > kMaxFetchSizeDefault) {
            ret_ = device_->WriteFail(android::base::StringPrintf(
                    "Cannot fetch 0x%" PRIx64
                    " bytes because it exceeds maximum transport size 0x%x",
                    partition_size_, kMaxDownloadSizeDefault));
            return false;
        }
        return true;
    }
    // Assume Open() returns true.
    // After execution, ret_ is set to the value that FetchHandler should return.
    void Fetch() {
        CHECK(start_offset_ <= std::numeric_limits<off64_t>::max());
        if (lseek64(handle_.fd(), start_offset_, SEEK_SET) != static_cast<off64_t>(start_offset_)) {
            ret_ = device_->WriteFail(android::base::StringPrintf(
                    "On partition %s, unable to lseek(0x%" PRIx64 ": %s", partition_name_.c_str(),
                    start_offset_, strerror(errno)));
            return;
        }
        if (!device_->WriteStatus(FastbootResult::DATA,
                                  android::base::StringPrintf(
                                          "%08x", static_cast<uint32_t>(total_size_to_read_)))) {
            ret_ = false;
            return;
        }
        uint64_t end_offset = start_offset_ + total_size_to_read_;
        std::vector<char> buf(1_MiB);
        uint64_t current_offset = start_offset_;
        while (current_offset < end_offset) {
            // On any error, exit. We can't return a status message to the driver because
            // we are in the middle of writing data, so just let the driver guess what's wrong
            // by ending the data stream prematurely.
            uint64_t remaining = end_offset - current_offset;
            uint64_t chunk_size = std::min<uint64_t>(buf.size(), remaining);
            if (!android::base::ReadFully(handle_.fd(), buf.data(), chunk_size)) {
                PLOG(ERROR) << std::hex << "Unable to read 0x" << chunk_size << " bytes from "
                            << partition_name_ << " @ offset 0x" << current_offset;
                ret_ = false;
                return;
            }
            if (!device_->HandleData(false /* is read */, buf.data(), chunk_size)) {
                PLOG(ERROR) << std::hex << "Unable to send 0x" << chunk_size << " bytes of "
                            << partition_name_ << " @ offset 0x" << current_offset;
                ret_ = false;
                return;
            }
            current_offset += chunk_size;
        }
        ret_ = device_->WriteOkay(android::base::StringPrintf(
                "Fetched %s (offset=0x%" PRIx64 ", size=0x%" PRIx64, partition_name_.c_str(),
                start_offset_, total_size_to_read_));
    }
    static constexpr std::array<const char*, 3> kAllowedPartitions{
            "vendor_boot",
            "vendor_boot_a",
            "vendor_boot_b",
    };
    FastbootDevice* device_;
    const std::vector<std::string>* args_ = nullptr;
    std::string partition_name_;
    PartitionHandle handle_;
    uint64_t partition_size_ = 0;
    uint64_t start_offset_ = 0;
    uint64_t total_size_to_read_ = 0;
    // What FetchHandler should return.
    std::optional<bool> ret_ = std::nullopt;
};
}  // namespace
bool FetchHandler(FastbootDevice* device, const std::vector<std::string>& args) {
    return PartitionFetcher::Fetch(device, args);
}
tree: bc578ef605815cf4bbc3d788961dd8fce25926b9 [path history] [tgz]
commands.cpp
commands.h
fastboot_device.cpp
fastboot_device.h
flashing.cpp
flashing.h
main.cpp
tcp_client.cpp
tcp_client.h
usb.cpp
usb.h
usb_client.cpp
usb_client.h
usb_iouring.cpp
usb_iouring.h
utility.cpp
utility.h
variables.cpp
variables.h
Examples
In the examples below, S indicates the starting client sequence number.

Host                                    Client
======================================================================
[Initialization, S = 0x55AA]
[Host: version 1, 2048-byte packets. Client: version 2, 1024-byte packets.]
[Resulting values to use: version = 1, max packet size = 1024]
ID   Flag SeqH SeqL Data                ID   Flag SeqH SeqL Data
----------------------------------------------------------------------
0x01 0x00 0x00 0x00
                                        0x01 0x00 0x00 0x00 0x55 0xAA
0x02 0x00 0x55 0xAA 0x00 0x01 0x08 0x00
                                        0x02 0x00 0x55 0xAA 0x00 0x02 0x04 0x00

----------------------------------------------------------------------
[fastboot "getvar" commands, S = 0x0001]
ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
----------------------------------------------------------------------
0x03  0x00  0x00  0x01  getvar:version
                                        0x03  0x00  0x00  0x01
0x03  0x00  0x00  0x02
                                        0x03  0x00  0x00  0x02  OKAY0.4
0x03  0x00  0x00  0x03  getvar:none
                                        0x03  0x00  0x00  0x03
0x03  0x00  0x00  0x04
                                        0x03  0x00  0x00  0x04  FAILUnknown var

----------------------------------------------------------------------
[fastboot "INFO" responses, S = 0x0000]
ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
----------------------------------------------------------------------
0x03  0x00  0x00  0x00  <command>
                                        0x03  0x00  0x00  0x00
0x03  0x00  0x00  0x01
                                        0x03  0x00  0x00  0x01  INFOWait1
0x03  0x00  0x00  0x02
                                        0x03  0x00  0x00  0x02  INFOWait2
0x03  0x00  0x00  0x03
                                        0x03  0x00  0x00  0x03  OKAY

----------------------------------------------------------------------
[Chunking 2100 bytes of data, max packet size = 1024, S = 0xFFFF]
ID   Flag SeqH SeqL Data                ID   Flag SeqH SeqL Data
----------------------------------------------------------------------
0x03 0x00 0xFF 0xFF download:0000834
                                        0x03 0x00 0xFF 0xFF
0x03 0x00 0x00 0x00
                                        0x03 0x00 0x00 0x00 DATA0000834
0x03 0x01 0x00 0x01 <1020 bytes>
                                        0x03 0x00 0x00 0x01
0x03 0x01 0x00 0x02 <1020 bytes>
                                        0x03 0x00 0x00 0x02
0x03 0x00 0x00 0x03 <60 bytes>
                                        0x03 0x00 0x00 0x03
0x03 0x00 0x00 0x04
                                        0x03 0x00 0x00 0x04 OKAY

----------------------------------------------------------------------
[Unknown ID error, S = 0x0000]
ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
----------------------------------------------------------------------
0x10  0x00  0x00  0x00
                                        0x00  0x00  0x00  0x00  <error message>

----------------------------------------------------------------------
[Host packet loss and retransmission, S = 0x0000]
ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
----------------------------------------------------------------------
0x03  0x00  0x00  0x00  getvar:version [lost]
0x03  0x00  0x00  0x00  getvar:version [lost]
0x03  0x00  0x00  0x00  getvar:version
                                        0x03  0x00  0x00  0x00
0x03  0x00  0x00  0x01
                                        0x03  0x00  0x00  0x01  OKAY0.4

----------------------------------------------------------------------
[Client packet loss and retransmission, S = 0x0000]
ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
----------------------------------------------------------------------
0x03  0x00  0x00  0x00  getvar:version
                                        0x03  0x00  0x00  0x00 [lost]
0x03  0x00  0x00  0x00  getvar:version
                                        0x03  0x00  0x00  0x00 [lost]
0x03  0x00  0x00  0x00  getvar:version
                                        0x03  0x00  0x00  0x00
0x03  0x00  0x00  0x01
                                        0x03  0x00  0x00  0x01  OKAY0.4

----------------------------------------------------------------------
[Host packet delayed, S = 0x0000]
ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
----------------------------------------------------------------------
0x03  0x00  0x00  0x00  getvar:version [delayed]
0x03  0x00  0x00  0x00  getvar:version
                                        0x03  0x00  0x00  0x00
0x03  0x00  0x00  0x01
                                        0x03  0x00  0x00  0x01  OKAY0.4
0x03  0x00  0x00  0x00  getvar:version [arrives late with old seq#, is ignored]
 Save
Packet Size
The maximum packet size is negotiated by the host and device in the Init packet. Devices must support at least 512-byte packets, but packet size has a direct correlation with download speed, so devices are strongly suggested to support at least 1024-byte packets. On a local network with 0.5ms round-trip time this will provide transfer rates of ~2MB/s. Over WiFi it will likely be significantly less.

Query and Initialization packets, which are sent before size negotiation is complete, must always be 512 bytes or less.

Packet Re-Transmission
The host will re-transmit any packet that does not receive a response. The requirement of exactly one device response packet per host packet is how we achieve reliability and in-order delivery of packets.

For simplicity of implementation, there is no windowing of multiple unacknowledged packets in this version of the protocol. The host will continue to send the same packet until a response is received. Windowing functionality may be implemented in future versions if necessary to increase performance.

The first Query packet will only be attempted a small number of times, but subsequent packets will attempt to retransmit for at least 1 minute before giving up. This means a device may safely ignore host UDP packets for up to 1 minute during long operations, e.g. writing to flash.

Continuation Packets
Any packet may set the continuation flag to indicate that the data is incomplete. Large data such as downloading an image may require many continuation packets. The receiver should respond to a continuation packet with an empty packet to acknowledge receipt. See examples below.

Summary
The host starts with a Query packet, then an Initialization packet, after which only Fastboot packets are sent. Fastboot packets may contain data from the host for writes, or from the device for reads, but not both.

Given a next expected sequence number S and a received packet P, the device behavior should be:

if P is a Query packet:
  * respond with a Query packet with S in the data field
else if P has sequence == S:
  * process P and take any required action
  * create a response packet R with the same ID and sequence as P, containing
    any response data required.
  * transmit R and save it in case of re-transmission
  * increment S
else if P has sequence == S - 1:
  * re-transmit the saved response packet R from above
else:
  * ignore the packet
 Save
Query
      The host sends a query packet once on startup to sync with the device.
      The host will not know the current sequence number, so the device must
      respond to all query packets regardless of sequence number.

      The response data field should contain a 2-byte big-endian value
      giving the next expected sequence number.

Init
      The host sends an init packet once the query response is returned. The
      device must abort any in-progress operation and prepare for a new
      fastboot session. This message is meant to allow recovery if a
      previous session failed, e.g. due to network error or user Ctrl+C.

      The data field contains two big-endian 2-byte values, a protocol
      version and the max UDP packet size (including the 4-byte header).
      Both the host and device will send these values, and in each case
      the minimum of the sent values must be used.

Fastboot
      These packets wrap the fastboot protocol. To write, the host will
      send a packet with fastboot data, and the device will reply with an
      empty packet as an ACK. To read, the host will send an empty packet,
      and the device will reply with fastboot data. The device may not give
      any data in the ACK packet.

Error
      The device may respond to any packet with an error packet to indicate
      a UDP protocol error. The data field should contain an ASCII string
      describing the error. This is the only case where a device is allowed
      to return a packet ID other than the one sent by the host.
UDP Protocol v1
The UDP protocol is more complex than TCP since we must implement reliability to ensure no packets are lost, but the general concept of wrapping the fastboot protocol is the same.

Overview:

As with TCP, the device will listen on UDP port 5554.
Maximum UDP packet size is negotiated during initialization.
The host drives all communication; the device may only send a packet as a response to a host packet.
If the host does not receive a response in 500ms it will re-transmit.
UDP Packet format
+----------+----+-------+-------+--------------------+
| Byte #   | 0  |   1   | 2 - 3 |  4+                |
+----------+----+-------+-------+--------------------+
| Contents | ID | Flags | Seq # | Data               |
+----------+----+-------+-------+--------------------+

ID      Packet ID:
          0x00: Error.
          0x01: Query.
          0x02: Initialization.
          0x03: Fastboot.

        Packet types are described in more detail below.

Flags   Packet flags: 0 0 0 0 0 0 0 C
          C=1 indicates a continuation packet; the data is too large and will
              continue in the next packet.

          Remaining bits are reserved for future use and must be set to 0.

Seq #   2-byte packet sequence number (big-endian). The host will increment
        this by 1 with each new packet, and the device must provide the
        corresponding sequence number in the response packets.

Data    Packet data, not present in all packets.
 Save
Where data_size is an unsigned 8-byte big-endian binary value, and data is the fastboot packet. The 8-byte length is intended to provide future-proofing even though currently fastboot packets have a 4-byte maximum length.

Example
In this example the fastboot host queries the device for two variables, “version” and “none”.

Host    <connect to the device on port 5555>
Host    FB01
Device  FB01
Host    [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x0E]getvar:version
Device  [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x07]OKAY0.4
Host    [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x0B]getvar:none
Device  [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x14]FAILUnknown variable
Host    <disconnect>
 Save
TCP Protocol v1
The TCP protocol is designed to be a simple way to use the fastboot protocol over ethernet if USB is not available.

The device will open a TCP server on port 5554 and wait for a fastboot client to connect.

Handshake
Upon connecting, both sides will send a 4-byte handshake message to ensure they are speaking the same protocol. This consists of the ASCII characters “FB” followed by a 2-digit base-10 ASCII version number. For example, the version 1 handshake message will be [FB01].

If either side detects a malformed handshake, it should disconnect.

The protocol version to use must be the minimum of the versions sent by each side; if either side cannot speak this protocol version, it should disconnect.

Fastboot Data
Once the handshake is complete, fastboot data will be sent as follows:

[data_size][data]
 Save
is-logical:%s       If the value is "yes", the partition is logical.
                    Otherwise the partition is physical.
update-super:%s:%s  Write the previously downloaded image to a super
                    partition. Unlike the "flash" command, this has
                    special rules. The image must have been created by
                    the lpmake command, and must not be a sparse image.
                    If the last argument is "wipe", then all existing
                    logical partitions are deleted. If no final argument
                    is specified, the partition tables are merged. Any
                    partition in the new image that does not exist in the
                    old image is created with a zero size.

                    In all cases, this will cause the temporary "scratch"
                    partition to be deleted if it exists.

create-logical-partition:%s:%d
                    Create a logical partition with the given name and
                    size, in the super partition.

delete-logical-partition:%s
                    Delete a logical partition with the given name.

resize-logical-partition:%s:%d
                    Change the size of the named logical partition.
version             Version of FastBoot protocol supported.
                    It should be "0.4" for this document.

version-bootloader  Version string for the Bootloader.

version-baseband    Version string of the Baseband Software

product             Name of the product

serialno            Product serial number

secure              If the value is "yes", this is a secure
                    bootloader requiring a signature before
                    it will install or boot images.

is-userspace        If the value is "yes", the device is running
                    fastbootd. Otherwise, it is running fastboot
                    in the bootloader.
After generating the list of tasks to execute, Fastboot will try and
optimize the flashing of the dynamic partitions by constructing an
optimized flash super task. Fastboot will explicitly pattern match the
following commands and try and concatenate it into this task. (doing so
will allow us to avoid the reboot into userspace fastbootd which takes
significant time)

//Optimizable Block
reboot fastboot
update-super                        ---> generate optimized flash super task
$FOR EACH {dynamic partition}
    flash {dynamic partition}
After generating the list of tasks to execute, Fastboot will try and
optimize the flashing of the dynamic partitions by constructing an
optimized flash super task. Fastboot will explicitly pattern match the
following commands and try and concatenate it into this task. (doing so
will allow us to avoid the reboot into userspace fastbootd which takes
significant time)

//Optimizable Block
reboot fastboot
update-super                        ---> generate optimized flash super task
$FOR EACH {dynamic partition}
    flash {dynamic partition}
flash %s           Flash a given partition. Optional arguments include
                   --slot-other, {filename_path}, --apply-vbmeta

reboot %s          Reboot to either bootloader or fastbootd

update-super       Updates the super partition

if-wipe            Conditionally run some other functionality if
                   wipe is specified

erase %s           Erase a given partition (can only be used in conjunction)
                   with if-wipe -> eg. if-wipe erase cache
getvar:%s          Read a config/version variable from the bootloader.
                   The variable contents will be returned after the
                   OKAY response. If the variable is unknown, the bootloader
                   should return a FAIL response, optionally with an error
                   message.

                   Previous versions of this document indicated that getvar
                   should return an empty OKAY response for unknown
                   variables, so older devices might exhibit this behavior,
                   but new implementations should return FAIL instead.

download:%08x      Write data to memory which will be later used
                   by "boot", "ramdisk", "flash", etc.  The client
                   will reply with "DATA%08x" if it has enough
                   space in RAM or "FAIL" if not.  The size of
                   the download is remembered.

upload             Read data from memory which was staged by the last
                   command, e.g. an oem command.  The client will reply
                   with "DATA%08x" if it is ready to send %08x bytes of
                   data.  If no data was staged in the last command,
                   the client must reply with "FAIL".  After the client
                   successfully sends %08x bytes, the client shall send
                   a single packet starting with "OKAY".  Clients
                   should not support "upload" unless it supports an
                   oem command that requires "upload" capabilities.

flash:%s           Write the previously downloaded image to the
                   named partition (if possible).

erase:%s           Erase the indicated partition (clear to 0xFFs)

boot               The previously downloaded data is a boot.img
                   and should be booted according to the normal
                   procedure for a boot.img

continue           Continue booting as normal (if possible)

reboot             Reboot the device.

reboot-bootloader
                   Reboot back into the bootloader.
                   Useful for upgrade processes that require upgrading
                   the bootloader and then upgrading other partitions
                   using the new bootloader.
Host:    "getvar:version"        request version variable

Client:  "OKAY0.4"               return version "0.4"

Host:    "getvar:nonexistant"    request some undefined variable

Client:  "FAILUnknown variable"  getvar failure; see getvar details below

Host:    "download:00001234"     request to send 0x1234 bytes of data

Client:  "DATA00001234"          ready to accept data

Host:    < 0x1234 bytes >        send data

Client:  "OKAY"                  success

Host:    "flash:bootloader"      request to flash the data to the bootloader

Client:  "INFOerasing flash"     indicate status / progress
         "INFOwriting flash"
         "OKAY"                  indicate success

Host:    "powerdown"             send a command

Client:  "FAILunknown command"   indicate failure
/*
 * Copyright (C) 2008 The Android Open Source Project
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/usbdevice_fs.h>
#include <linux/version.h>
#include <linux/usb/ch9.h>
#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <chrono>
#include <memory>
#include <thread>
#include "usb.h"
#include "util.h"
using namespace std::chrono_literals;
#define MAX_RETRIES 2
/* Timeout in seconds for usb_wait_for_disconnect.
 * It doesn't usually take long for a device to disconnect (almost always
 * under 2 seconds) but we'll time out after 3 seconds just in case.
 */
#define WAIT_FOR_DISCONNECT_TIMEOUT  3
#ifdef TRACE_USB
#define DBG1(x...) fprintf(stderr, x)
#define DBG(x...) fprintf(stderr, x)
#else
#define DBG(x...)
#define DBG1(x...)
#endif
// Kernels before 3.3 have a 16KiB transfer limit. That limit was replaced
// with a 16MiB global limit in 3.3, but each URB submitted required a
// contiguous kernel allocation, so you would get ENOMEM if you tried to
// send something larger than the biggest available contiguous kernel
// memory region. 256KiB contiguous allocations are generally not reliable
// on a device kernel that has been running for a while fragmenting its
// memory, but that shouldn't be a problem for fastboot on the host.
// In 3.6, the contiguous buffer limit was removed by allocating multiple
// 16KiB chunks and having the USB driver stitch them back together while
// transmitting using a scatter-gather list, so 256KiB bulk transfers should
// be reliable.
// 256KiB seems to work, but 1MiB bulk transfers lock up my z620 with a 3.13
// kernel.
// 128KiB was experimentally found to be enough to saturate the bus at
// SuperSpeed+, so we first try double that for writes. If the operation fails
// due to a lack of contiguous regions (or an ancient kernel), try smaller sizes
// until we find one that works (see LinuxUsbTransport::Write). Reads are less
// performance critical so for now just use a known good size.
#define MAX_USBFS_BULK_WRITE_SIZE (256 * 1024)
#define MAX_USBFS_BULK_READ_SIZE (16 * 1024)
// This size should pretty much always work (it's compatible with pre-3.3
// kernels and it's what we used to use historically), so if it doesn't work
// something has gone badly wrong.
#define MIN_USBFS_BULK_WRITE_SIZE (16 * 1024)
struct usb_handle
{
    char fname[64];
    int desc;
    unsigned char ep_in;
    unsigned char ep_out;
};
class LinuxUsbTransport : public UsbTransport {
  public:
    explicit LinuxUsbTransport(std::unique_ptr<usb_handle> handle, uint32_t ms_timeout = 0)
        : handle_(std::move(handle)), ms_timeout_(ms_timeout) {}
    ~LinuxUsbTransport() override;
    ssize_t Read(void* data, size_t len) override;
    ssize_t Write(const void* data, size_t len) override;
    int Close() override;
    int Reset() override;
    int WaitForDisconnect() override;
  private:
    std::unique_ptr<usb_handle> handle_;
    const uint32_t ms_timeout_;
    size_t max_usbfs_bulk_write_size_ = MAX_USBFS_BULK_WRITE_SIZE;
    DISALLOW_COPY_AND_ASSIGN(LinuxUsbTransport);
};
/* True if name isn't a valid name for a USB device in /sys/bus/usb/devices.
 * Device names are made up of numbers, dots, and dashes, e.g., '7-1.5'.
 * We reject interfaces (e.g., '7-1.5:1.0') and host controllers (e.g. 'usb1').
 * The name must also start with a digit, to disallow '.' and '..'
 */
static inline int badname(const char *name)
{
    if (!isdigit(*name))
      return 1;
    while(*++name) {
        if(!isdigit(*name) && *name != '.' && *name != '-')
            return 1;
    }
    return 0;
}
static int check(void *_desc, int len, unsigned type, int size)
{
    struct usb_descriptor_header *hdr = (struct usb_descriptor_header *)_desc;
    if(len < size) return -1;
    if(hdr->bLength < size) return -1;
    if(hdr->bLength > len) return -1;
    if(hdr->bDescriptorType != type) return -1;
    return 0;
}
static int filter_usb_device(char* sysfs_name,
                             char *ptr, int len, int writable,
                             ifc_match_func callback,
                             int *ept_in_id, int *ept_out_id, int *ifc_id)
{
    struct usb_device_descriptor *dev;
    struct usb_config_descriptor *cfg;
    struct usb_interface_descriptor *ifc;
    struct usb_endpoint_descriptor *ept;
    struct usb_ifc_info info;
    int in, out;
    unsigned i;
    unsigned e;
    if (check(ptr, len, USB_DT_DEVICE, USB_DT_DEVICE_SIZE))
        return -1;
    dev = (struct usb_device_descriptor *)ptr;
    len -= dev->bLength;
    ptr += dev->bLength;
    if (check(ptr, len, USB_DT_CONFIG, USB_DT_CONFIG_SIZE))
        return -1;
    cfg = (struct usb_config_descriptor *)ptr;
    len -= cfg->bLength;
    ptr += cfg->bLength;
    info.dev_vendor = dev->idVendor;
    info.dev_product = dev->idProduct;
    info.dev_class = dev->bDeviceClass;
    info.dev_subclass = dev->bDeviceSubClass;
    info.dev_protocol = dev->bDeviceProtocol;
    info.writable = writable;
    snprintf(info.device_path, sizeof(info.device_path), "usb:%s", sysfs_name);
    /* Read device serial number (if there is one).
     * We read the serial number from sysfs, since it's faster and more
     * reliable than issuing a control pipe read, and also won't
     * cause problems for devices which don't like getting descriptor
     * requests while they're in the middle of flashing.
     */
    info.serial_number[0] = '\0';
    if (dev->iSerialNumber) {
        char path[80];
        int fd;
        snprintf(path, sizeof(path),
                 "/sys/bus/usb/devices/%s/serial", sysfs_name);
        path[sizeof(path) - 1] = '\0';
        fd = open(path, O_RDONLY);
        if (fd >= 0) {
            int chars_read = read(fd, info.serial_number,
                                  sizeof(info.serial_number) - 1);
            close(fd);
            if (chars_read <= 0)
                info.serial_number[0] = '\0';
            else if (info.serial_number[chars_read - 1] == '\n') {
                // strip trailing newline
                info.serial_number[chars_read - 1] = '\0';
            }
        }
    }
    for(i = 0; i < cfg->bNumInterfaces; i++) {
        while (len > 0) {
	        struct usb_descriptor_header *hdr = (struct usb_descriptor_header *)ptr;
            if (check(hdr, len, USB_DT_INTERFACE, USB_DT_INTERFACE_SIZE) == 0)
                break;
            len -= hdr->bLength;
            ptr += hdr->bLength;
        }
        if (len <= 0)
            return -1;
        ifc = (struct usb_interface_descriptor *)ptr;
        len -= ifc->bLength;
        ptr += ifc->bLength;
        in = -1;
        out = -1;
        info.ifc_class = ifc->bInterfaceClass;
        info.ifc_subclass = ifc->bInterfaceSubClass;
        info.ifc_protocol = ifc->bInterfaceProtocol;
        for(e = 0; e < ifc->bNumEndpoints; e++) {
            while (len > 0) {
	            struct usb_descriptor_header *hdr = (struct usb_descriptor_header *)ptr;
                if (check(hdr, len, USB_DT_ENDPOINT, USB_DT_ENDPOINT_SIZE) == 0)
                    break;
                len -= hdr->bLength;
                ptr += hdr->bLength;
            }
            if (len < 0) {
                break;
            }
            ept = (struct usb_endpoint_descriptor *)ptr;
            len -= ept->bLength;
            ptr += ept->bLength;
            if((ept->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK)
                continue;
            if(ept->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
                in = ept->bEndpointAddress;
            } else {
                out = ept->bEndpointAddress;
            }
            // For USB 3.0 devices skip the SS Endpoint Companion descriptor
            if (check((struct usb_descriptor_hdr *)ptr, len,
                      USB_DT_SS_ENDPOINT_COMP, USB_DT_SS_EP_COMP_SIZE) == 0) {
                len -= USB_DT_SS_EP_COMP_SIZE;
                ptr += USB_DT_SS_EP_COMP_SIZE;
            }
        }
        info.has_bulk_in = (in != -1);
        info.has_bulk_out = (out != -1);
        std::string interface;
        auto path = android::base::StringPrintf("/sys/bus/usb/devices/%s/%s:1.%d/interface",
                                                sysfs_name, sysfs_name, ifc->bInterfaceNumber);
        if (android::base::ReadFileToString(path, &interface)) {
            if (!interface.empty() && interface.back() == '\n') {
                interface.pop_back();
            }
            snprintf(info.interface, sizeof(info.interface), "%s", interface.c_str());
        }
        if(callback(&info) == 0) {
            *ept_in_id = in;
            *ept_out_id = out;
            *ifc_id = ifc->bInterfaceNumber;
            return 0;
        }
    }
    return -1;
}
static int read_sysfs_string(const char *sysfs_name, const char *sysfs_node,
                             char* buf, int bufsize)
{
    char path[80];
    int fd, n;
    snprintf(path, sizeof(path),
             "/sys/bus/usb/devices/%s/%s", sysfs_name, sysfs_node);
    path[sizeof(path) - 1] = '\0';
    fd = open(path, O_RDONLY);
    if (fd < 0)
        return -1;
    n = read(fd, buf, bufsize - 1);
    close(fd);
    if (n < 0)
        return -1;
    buf[n] = '\0';
    return n;
}
static int read_sysfs_number(const char *sysfs_name, const char *sysfs_node)
{
    char buf[16];
    int value;
    if (read_sysfs_string(sysfs_name, sysfs_node, buf, sizeof(buf)) < 0)
        return -1;
    if (sscanf(buf, "%d", &value) != 1)
        return -1;
    return value;
}
/* Given the name of a USB device in sysfs, get the name for the same
 * device in devfs. Returns 0 for success, -1 for failure.
 */
static int convert_to_devfs_name(const char* sysfs_name,
                                 char* devname, int devname_size)
{
    int busnum, devnum;
    busnum = read_sysfs_number(sysfs_name, "busnum");
    if (busnum < 0)
        return -1;
    devnum = read_sysfs_number(sysfs_name, "devnum");
    if (devnum < 0)
        return -1;
    snprintf(devname, devname_size, "/dev/bus/usb/%03d/%03d", busnum, devnum);
    return 0;
}
static std::unique_ptr<usb_handle> find_usb_device(const char* base, ifc_match_func callback)
{
    std::unique_ptr<usb_handle> usb;
    char devname[64];
    char desc[1024];
    int n, in, out, ifc;
    struct dirent *de;
    int fd;
    int writable;
    std::unique_ptr<DIR, decltype(&closedir)> busdir(opendir(base), closedir);
    if (busdir == 0) return 0;
    while ((de = readdir(busdir.get())) && (usb == nullptr)) {
        if (badname(de->d_name)) continue;
        if (!convert_to_devfs_name(de->d_name, devname, sizeof(devname))) {
//            DBG("[ scanning %s ]\n", devname);
            writable = 1;
            if ((fd = open(devname, O_RDWR)) < 0) {
                // Check if we have read-only access, so we can give a helpful
                // diagnostic like "adb devices" does.
                writable = 0;
                if ((fd = open(devname, O_RDONLY)) < 0) {
                    continue;
                }
            }
            n = read(fd, desc, sizeof(desc));
            if (filter_usb_device(de->d_name, desc, n, writable, callback, &in, &out, &ifc) == 0) {
                usb.reset(new usb_handle());
                strcpy(usb->fname, devname);
                usb->ep_in = in;
                usb->ep_out = out;
                usb->desc = fd;
                n = ioctl(fd, USBDEVFS_CLAIMINTERFACE, &ifc);
                if (n != 0) {
                    close(fd);
                    usb.reset();
                    continue;
                }
            } else {
                close(fd);
            }
        }
    }
    return usb;
}
LinuxUsbTransport::~LinuxUsbTransport() {
    Close();
}
ssize_t LinuxUsbTransport::Write(const void* _data, size_t len)
{
    unsigned char *data = (unsigned char*) _data;
    unsigned count = 0;
    struct usbdevfs_urb urb[2] = {};
    bool pending[2] = {};
    if (handle_->ep_out == 0 || handle_->desc == -1) {
        return -1;
    }
    auto submit_urb = [&](size_t i) {
        while (true) {
            int xfer = (len > max_usbfs_bulk_write_size_) ? max_usbfs_bulk_write_size_ : len;
            urb[i].type = USBDEVFS_URB_TYPE_BULK;
            urb[i].endpoint = handle_->ep_out;
            urb[i].buffer_length = xfer;
            urb[i].buffer = data;
            urb[i].usercontext = (void *)i;
            int n = ioctl(handle_->desc, USBDEVFS_SUBMITURB, &urb[i]);
            if (n != 0) {
                if (errno == ENOMEM && max_usbfs_bulk_write_size_ > MIN_USBFS_BULK_WRITE_SIZE) {
                    max_usbfs_bulk_write_size_ /= 2;
                    continue;
                }
                DBG("ioctl(USBDEVFS_SUBMITURB) failed\n");
                return false;
            }
            pending[i] = true;
            count += xfer;
            len -= xfer;
            data += xfer;
            return true;
        }
    };
    auto reap_urb = [&](size_t i) {
        while (pending[i]) {
            struct usbdevfs_urb *urbp;
            int res = ioctl(handle_->desc, USBDEVFS_REAPURB, &urbp);
            if (res != 0) {
                DBG("ioctl(USBDEVFS_REAPURB) failed\n");
                return false;
            }
            size_t done = (size_t)urbp->usercontext;
            if (done >= 2 || !pending[done]) {
                DBG("unexpected urb\n");
                return false;
            }
            if (urbp->status != 0 || urbp->actual_length != urbp->buffer_length) {
                DBG("urb returned error\n");
                return false;
            }
            pending[done] = false;
        }
        return true;
    };
    if (!submit_urb(0)) {
        return -1;
    }
    while (len > 0) {
        if (!submit_urb(1)) {
            return -1;
        }
        if (!reap_urb(0)) {
            return -1;
        }
        if (len <= 0) {
            if (!reap_urb(1)) {
                return -1;
            }
            return count;
        }
        if (!submit_urb(0)) {
            return -1;
        }
        if (!reap_urb(1)) {
            return -1;
        }
    }
    if (!reap_urb(0)) {
        return -1;
    }
    return count;
}
ssize_t LinuxUsbTransport::Read(void* _data, size_t len)
{
    unsigned char *data = (unsigned char*) _data;
    unsigned count = 0;
    struct usbdevfs_bulktransfer bulk;
    int n, retry;
    if (handle_->ep_in == 0 || handle_->desc == -1) {
        return -1;
    }
    while (len > 0) {
        int xfer = (len > MAX_USBFS_BULK_READ_SIZE) ? MAX_USBFS_BULK_READ_SIZE : len;
        bulk.ep = handle_->ep_in;
        bulk.len = xfer;
        bulk.data = data;
        bulk.timeout = ms_timeout_;
        retry = 0;
        do {
            DBG("[ usb read %d fd = %d], fname=%s\n", xfer, handle_->desc, handle_->fname);
            n = ioctl(handle_->desc, USBDEVFS_BULK, &bulk);
            DBG("[ usb read %d ] = %d, fname=%s, Retry %d \n", xfer, n, handle_->fname, retry);
            if (n < 0) {
                DBG1("ERROR: n = %d, errno = %d (%s)\n",n, errno, strerror(errno));
                if (++retry > MAX_RETRIES) return -1;
                std::this_thread::sleep_for(100ms);
            }
        } while (n < 0);
        count += n;
        len -= n;
        data += n;
        if(n < xfer) {
            break;
        }
    }
    return count;
}
int LinuxUsbTransport::Close()
{
    int fd;
    fd = handle_->desc;
    handle_->desc = -1;
    if(fd >= 0) {
        close(fd);
        DBG("[ usb closed %d ]\n", fd);
    }
    return 0;
}
int LinuxUsbTransport::Reset() {
    int ret = 0;
    // We reset the USB connection
    if ((ret = ioctl(handle_->desc, USBDEVFS_RESET, 0))) {
        return ret;
    }
    return 0;
}
std::unique_ptr<UsbTransport> usb_open(ifc_match_func callback, uint32_t timeout_ms) {
    std::unique_ptr<UsbTransport> result;
    std::unique_ptr<usb_handle> handle = find_usb_device("/sys/bus/usb/devices", callback);
    if (handle) {
        result = std::make_unique<LinuxUsbTransport>(std::move(handle), timeout_ms);
    }
    return result;
}
/* Wait for the system to notice the device is gone, so that a subsequent
 * fastboot command won't try to access the device before it's rebooted.
 * Returns 0 for success, -1 for timeout.
 */
int LinuxUsbTransport::WaitForDisconnect()
{
  double deadline = now() + WAIT_FOR_DISCONNECT_TIMEOUT;
  while (now() < deadline) {
    if (access(handle_->fname, F_OK)) return 0;
    std::this_thread::sleep_for(50ms);
  }
  return -1;
}
device/
fuzzer/
fuzzy_fastboot/
testdata/
.clang-format
⇨ ../.clang-format-4
Android.bp
Android.mk
bootimg_utils.cpp
bootimg_utils.h
constants.h
fastboot.bash
fastboot.cpp
fastboot.h
fastboot_driver.cpp
fastboot_driver.h
fastboot_driver_interface.h
fastboot_driver_mock.h
fastboot_driver_test.cpp
fastboot_integration_test.xml
fastboot_test.cpp
filesystem.cpp
filesystem.h
fs.cpp
fs.h
LICENSE
main.cpp
mock_transport.h
OWNERS
README.md
result.h
socket.cpp
socket.h
socket_mock.cpp
socket_mock.h
socket_test.cpp
storage.cpp
storage.h
super_flash_helper.cpp
super_flash_helper.h
super_flash_helper_test.cpp
task.cpp
task.h
task_test.cpp
tcp.cpp
tcp.h
tcp_test.cpp
test_fastboot.py
TEST_MAPPING
transport.h
udp.cpp
udp.h
udp_test.cpp
usb.h
usb_linux.cpp
usb_osx.cpp
usb_windows.cpp
util.cpp
util.h
vendor_boot_img_utils.cpp
vendor_boot_img_utils.h
vendor_boot_img_utils_test.cpp
#pragma once
#include <inttypes.h>
#include <stdlib.h>
#include <string>
#include <vector>
#include <android-base/unique_fd.h>
#include <bootimg.h>
#include <liblp/liblp.h>
#include <sparse/sparse.h>
using SparsePtr = std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)>;
/* util stuff */
double now();
void set_verbose();
// These printf-like functions are implemented in terms of vsnprintf, so they
// use the same attribute for compile-time format string checking.
void die(const char* fmt, ...) __attribute__((__noreturn__))
__attribute__((__format__(__printf__, 1, 2)));
void verbose(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
void die(const std::string& str) __attribute__((__noreturn__));
bool should_flash_in_userspace(const android::fs_mgr::LpMetadata& metadata,
                               const std::string& partition_name);
bool is_sparse_file(android::base::borrowed_fd fd);
int64_t get_file_size(android::base::borrowed_fd fd);
std::string fb_fix_numeric_var(std::string var);
class ImageSource {
  public:
    virtual ~ImageSource(){};
    virtual bool ReadFile(const std::string& name, std::vector<char>* out) const = 0;
    virtual android::base::unique_fd OpenFile(const std::string& name) const = 0;
};
#cd to the relevant file
cd /usr/share/applications
#us nano text editor on the proper file
sudo nano yourapp.desktop
#Make entry into the file and save it 
[Desktop Entry]
Name=YourAppName
Exec=/path/to/your/executable
Icon=/path/to/your/icon
Type=Application
Terminal=false
add_action( 'admin_menu', 'my_custom_menu_page' );
function my_custom_menu_page() {
add_menu_page(
__( 'Custom Menu Title', 'textdomain' ),
'Custom Menu',
'manage_options',
'custompage',
'my_custom_menu_page_content',
'dashicons-admin-generic',
);
}

add_action( 'admin_menu', 'my_custom_submenu_page' );
function my_custom_submenu_page() {
add_submenu_page(
'custompage',
__( 'Custom Submenu Title', 'textdomain' ),
'Custom Submenu',
'manage_options',
'customsubmenu',
'my_custom_submenu_page_content'
);
}
 function direction(item) {
    const increment =
      item.classList[1] === "decrease"
        ? counter--
        : item.classList[1] === "increase"
        ? counter++
        : item.classList[1] === "reset"
        ? (counter = 0)
        : counter; 

    return increment;
  }
function hide_dashboard_widgets() {
    echo '<style>#dashboard-widgets-wrap { display: none !important; }</style>';
}
add_action('admin_head', 'hide_dashboard_widgets');
import 'package:flutter/material.dart';
import 'package:shalestin/Screens/home.dart';
  
  void main() => runApp(const MyApp());
  
  class MyApp extends StatelessWidget {
    const MyApp({super.key});
  
    @override
    Widget build(BuildContext context) {
      return MaterialApp(
 routes: {
        'Home': (context) => const Home(),
      },
        initialRoute: 'Home',
        debugShowCheckedModeBanner: false,

      );
    }
  }
import io
import pandas as pd
import requests
if 'data_loader' not in globals():
    from mage_ai.data_preparation.decorators import data_loader
if 'test' not in globals():
    from mage_ai.data_preparation.decorators import test


@data_loader
def load_data_from_api(*args, **kwargs):
    """
    Template for loading data from API
    """
    url = 'https://github.com/DataTalksClub/nyc-tlc-data/releases/download/yellow/yellow_tripdata_2021-01.csv.gz'
    
    taxi_dtypes = {
        'VendorID': pd.Int64Dtype(),
        'passenger_count': pd.Int64Dtype(),
        'trip_distance': float,
        'RatecodeID': pd.Int64Dtype(),
        'store_and_fwd_flag': str,
        'PULocationID': pd.Int64Dtype(),
        'DOLocationID': pd.Int64Dtype(),
        'payment_type': pd.Int64Dtype(),
        'fare_amount': float,
        'extra': float,
        'mta_tax': float,
        'tip_amount': float,
        'tolls_amount': float,
        'improvement_surcharge': float,
        'total_amount': float,
        'congestion_surcharge': float
    }

    parse_dates = ['tpep_pickup_datetime', 'tpep_dropoff_datetime']

    return pd.read_csv(url, sep=",", compression="gzip", dtype=taxi_dtypes, parse_dates=parse_dates)

@test
def test_output(output, *args) -> None:
    """
    Template code for testing the output of the block.
    """
    assert output is not None, 'The output is undefined'
    //Use the ability if it is ready.
    public bool Use(int abilityNum)
    {
        //Is it ready?
        if (IsReady() == false)
            return false;
        //Use the power.
        ParentHero.UsePower(PowerUsed);
        //Apply the damage (or healing is the damage is negative).
        if (ParentHero.Target.TakeDamage(DamageDone) == true)
            ParentHero.Target = ParentHero.FindTarget(); //If the target is dead, find a new one.

        //TODO: Add needed flags or other functionality for abilities that don't just do
        //damage or affect more than one target (AoE, heals, dodges, blocks, stuns, etc.)
        abilityUses += 1.0f;
        
        //if the ability is the Flipper Smash, Knockback the enemy
        if(abilityNum == 2)
        {
            //change position of enemy 2 units to the right
            ParentHero.Target.transform.position = new Vector3(ParentHero.Target.transform.position.x + 2.0f, ParentHero.Target.transform.position.y, ParentHero.Target.transform.position.z);
        }

        //if ability is Fur Bomb, AoE attack
        if(abilityNum == 5)
        {
            //find all enemies
            var enemies = FindObjectsOfType<Enemy>();
            //go check all the enemies in round
            foreach (Enemy enemy in enemies)
            {
                //if there are other enemies within range
                if (enemy.transform.position.x <= (ParentHero.transform.position.x + 10.0f) && enemy.transform.position.x >= (ParentHero.transform.position.x - 10.0f))
                {
                    //and is not the already targetted enemy
                    if(enemy != ParentHero.Target)
                    {
                        //take decreased damage
                        enemy.TakeDamage(DamageDone - 5.0f);
                    }
                }
            }
        }

        //Put the ability on cooldown.
        CooldownLeft = CooldownTime;
        return true;
    }
<div class="col-xs-3">
    <?= $form->field($model, 'fecha')->widget(
            DatePicker::classname(),
            [
                'language' => 'es',
                'removeButton' => false,
                'options' => [
                    'placeholder' => 'Fecha:',
                    'class' => 'form-control',
                    'id' => 'fecha_desde-input',
                    'onchange' => 'buscarProyeccion(this.value)'
                ],
                'pluginOptions' =>
                [
                    'startDate' => '01-01-2000',
                    //'startDate' => date('d-m-Y'),
                    'autoclose' => true,
                    'format' => 'dd-mm-yyyy',
                ]
            ]
            )->label('Fecha'); ?>

    </div>
SELECT
  `tabGL Entry`.`posting_date` AS `posting_date`,                -- 1
  `tabGL Entry`.`voucher_type` AS `voucher_type`,                -- 2
  `tabGL Entry`.`voucher_no` AS `voucher_no`,                    -- 3
  `tabGL Entry`.`account` AS `account`,                          -- 4
  `tabGL Entry`.`party` AS `party`,                              -- 5
  `tabGL Entry`.`against` AS `against`,                          -- 6
  `tabSales Invoice Item`.`item_name` AS `sales_item`,           -- 7
  LPAD(FORMAT(`tabSales Invoice Item`.`qty`, 2), 15, ' ') AS `sales_qty`,  -- 8 (right-aligned)
  FORMAT(`tabSales Invoice Item`.`rate`, 2) AS `sales_rate`,     -- 9
  `tabPurchase Invoice Item`.`item_name` AS `pur_item`,          -- 10
  LPAD(FORMAT(`tabPurchase Invoice Item`.`qty`, 2), 15, ' ') AS `pur_qty`,  -- 11 (right-aligned)
  FORMAT(`tabPurchase Invoice Item`.`rate`, 2) AS `pur_rate`,    -- 12
  LPAD(FORMAT(`tabGL Entry`.`debit`, 2), 15, ' ') AS `debit`,    -- 13 (right-aligned)
  LPAD(FORMAT(`tabGL Entry`.`credit`, 2), 15, ' ') AS `credit`,  -- 14 (right-aligned)
  LPAD(
    FORMAT(
      SUM(`tabGL Entry`.`debit` - `tabGL Entry`.`credit`) OVER (
        PARTITION BY `tabGL Entry`.`account`, `tabGL Entry`.`party`
        ORDER BY `tabGL Entry`.`posting_date`, `tabGL Entry`.`voucher_no`, `tabGL Entry`.`name`
      ),
      2
    ),
    15,
    ' '
  ) AS `balance`                                                 -- 15 (right-aligned)
FROM
  `tabGL Entry`
LEFT JOIN `tabSales Invoice Item` ON `tabGL Entry`.`voucher_no` = `tabSales Invoice Item`.`parent`
LEFT JOIN `tabPurchase Invoice Item` ON `tabGL Entry`.`voucher_no` = `tabPurchase Invoice Item`.`parent`
WHERE
  `tabGL Entry`.`posting_date` BETWEEN %(start_date)s AND %(end_date)s
  AND `tabGL Entry`.`account` LIKE %(account)s
  AND `tabGL Entry`.`company` = %(company)s
  AND `tabGL Entry`.`party_type` = %(party_type)s
  AND `tabGL Entry`.`party` = %(party)s
  AND `tabGL Entry`.`is_cancelled` = 0
ORDER BY
  `tabGL Entry`.`posting_date` ASC,
  `tabGL Entry`.`voucher_no` ASC,
  `tabGL Entry`.`name` ASC;
------------------------------------------------------------------------------


Filters:::::

No.
Label
Fieldtype
Fieldname
Mandatory
Options

1
start_date
Date
start_date


2
end_date
Date
end_date


3
company
Link
company

Company

4
Account
Link
account

Account

5
party_type
Link
party_type

Party Type

6
Party
Dynamic Link
party

party_type

SELECT
  `tabGL Entry`.`posting_date` AS `posting_date`,                -- 1
  `tabGL Entry`.`voucher_type` AS `voucher_type`,                -- 2
  `tabGL Entry`.`voucher_no` AS `voucher_no`,                    -- 3
  `tabGL Entry`.`account` AS `account`,                          -- 4
  `tabGL Entry`.`party` AS `party`,                              -- 5
  `tabGL Entry`.`against` AS `against`,                          -- 6
  `tabSales Invoice Item`.`item_name` AS `sales_item`,           -- 7
  LPAD(FORMAT(`tabSales Invoice Item`.`qty`, 2), 15, ' ') AS `sales_qty`,  -- 8 (right-aligned)
  FORMAT(`tabSales Invoice Item`.`rate`, 2) AS `sales_rate`,     -- 9
  LPAD(FORMAT(`tabGL Entry`.`debit`, 2), 15, ' ') AS `debit`,    -- 13 (right-aligned)
  LPAD(FORMAT(`tabGL Entry`.`credit`, 2), 15, ' ') AS `credit`,  -- 14 (right-aligned)
  LPAD(
    FORMAT(
      SUM(`tabGL Entry`.`debit` - `tabGL Entry`.`credit`) OVER (
        PARTITION BY `tabGL Entry`.`account`, `tabGL Entry`.`party`
        ORDER BY `tabGL Entry`.`posting_date`, `tabGL Entry`.`voucher_no`, `tabGL Entry`.`name`
      ),
      2
    ),
    15,
    ' '
  ) AS `balance`                                                 -- 15 (right-aligned)
FROM
  `tabGL Entry`
LEFT JOIN `tabSales Invoice Item` ON `tabGL Entry`.`voucher_no` = `tabSales Invoice Item`.`parent`
WHERE
  `tabGL Entry`.`posting_date` BETWEEN %(start_date)s AND %(end_date)s
  AND `tabGL Entry`.`account` LIKE %(account)s
  AND `tabGL Entry`.`company` = %(company)s
  AND `tabGL Entry`.`party_type` = %(party_type)s
  AND `tabGL Entry`.`party` = %(party)s
  AND `tabGL Entry`.`is_cancelled` = 0
ORDER BY
  `tabGL Entry`.`posting_date` ASC,
  `tabGL Entry`.`voucher_no` ASC,
  `tabGL Entry`.`name` ASC;

--------------------------------------------------


No.
Label
Fieldtype
Fieldname
Mandatory
Options

1
start_date
Date
start_date


2
end_date
Date
end_date


3
company
Link
company

Company

4
Account
Link
account

Account

5
party_type
Link
party_type

Party Type

6
Party
Dynamic Link
party

party_type
SELECT
  `tabGL Entry`.`posting_date` AS `posting_date`,                -- 1
  `tabGL Entry`.`voucher_type` AS `voucher_type`,                -- 2
  `tabGL Entry`.`voucher_no` AS `voucher_no`,                    -- 3
  `tabGL Entry`.`account` AS `account`,                          -- 4
  `tabGL Entry`.`party` AS `party`,                              -- 5
  `tabGL Entry`.`against` AS `against`,                          -- 6
  `tabPurchase Invoice Item`.`item_name` AS `pur_item`,          -- 10
  LPAD(FORMAT(`tabPurchase Invoice Item`.`qty`, 2), 15, ' ') AS `pur_qty`,  -- 11 (right-aligned)
  FORMAT(`tabPurchase Invoice Item`.`rate`, 2) AS `pur_rate`,    -- 12
  LPAD(FORMAT(`tabGL Entry`.`debit`, 2), 15, ' ') AS `debit`,    -- 13 (right-aligned)
  LPAD(FORMAT(`tabGL Entry`.`credit`, 2), 15, ' ') AS `credit`,  -- 14 (right-aligned)
  LPAD(
    FORMAT(
      SUM(`tabGL Entry`.`debit` - `tabGL Entry`.`credit`) OVER (
        PARTITION BY `tabGL Entry`.`account`, `tabGL Entry`.`party`
        ORDER BY `tabGL Entry`.`posting_date`, `tabGL Entry`.`voucher_no`, `tabGL Entry`.`name`
      ),
      2
    ),
    15,
    ' '
  ) AS `balance`                                                 -- 15 (right-aligned)
FROM
  `tabGL Entry`
LEFT JOIN `tabPurchase Invoice Item` ON `tabGL Entry`.`voucher_no` = `tabPurchase Invoice Item`.`parent`
WHERE
  `tabGL Entry`.`posting_date` BETWEEN %(start_date)s AND %(end_date)s
  AND `tabGL Entry`.`account` LIKE %(account)s
  AND `tabGL Entry`.`company` = %(company)s
  AND `tabGL Entry`.`party_type` = %(party_type)s
  AND `tabGL Entry`.`party` = %(party)s
  AND `tabGL Entry`.`is_cancelled` = 0
ORDER BY
  `tabGL Entry`.`posting_date` ASC,
  `tabGL Entry`.`voucher_no` ASC,
  `tabGL Entry`.`name` ASC;






No.
Label
Fieldtype
Fieldname
Mandatory
Options

1
start_date
Date
start_date


2
end_date
Date
end_date


3
company
Link
company

Company

4
Account
Link
account

Account

5
party_type
Link
party_type

Party Type

6
Party
Dynamic Link
party

party_type

SELECT
  `tabGL Entry`.`posting_date` AS `posting_date`,                -- 1
  `tabGL Entry`.`voucher_type` AS `voucher_type`,                -- 2
  `tabGL Entry`.`voucher_no` AS `voucher_no`,                    -- 3
  `tabGL Entry`.`account` AS `account`,                          -- 4
  `tabGL Entry`.`party` AS `party`,                              -- 5
  `tabGL Entry`.`against` AS `against`,                          -- 6
  `tabSales Invoice Item`.`item_name` AS `sales_item`,           -- 7
  LPAD(FORMAT(`tabSales Invoice Item`.`qty`, 2), 15, ' ') AS `sales_qty`,  -- 8 (right-aligned)
  FORMAT(`tabSales Invoice Item`.`rate`, 2) AS `sales_rate`,     -- 9
  `tabPurchase Invoice Item`.`item_name` AS `pur_item`,          -- 10
  LPAD(FORMAT(`tabPurchase Invoice Item`.`qty`, 2), 15, ' ') AS `pur_qty`,  -- 11 (right-aligned)
  FORMAT(`tabPurchase Invoice Item`.`rate`, 2) AS `pur_rate`,    -- 12
  LPAD(FORMAT(`tabGL Entry`.`debit`, 2), 15, ' ') AS `debit`,    -- 13 (right-aligned)
  LPAD(FORMAT(`tabGL Entry`.`credit`, 2), 15, ' ') AS `credit`,  -- 14 (right-aligned)
  LPAD(
    FORMAT(
      SUM(`tabGL Entry`.`debit` - `tabGL Entry`.`credit`) OVER (
        PARTITION BY `tabGL Entry`.`account`, `tabGL Entry`.`party`
        ORDER BY `tabGL Entry`.`posting_date`, `tabGL Entry`.`voucher_no`, `tabGL Entry`.`name`
      ),
      2
    ),
    15,
    ' '
  ) AS `balance`                                                 -- 15 (right-aligned)
FROM
  `tabGL Entry`
LEFT JOIN `tabSales Invoice Item` ON `tabGL Entry`.`voucher_no` = `tabSales Invoice Item`.`parent`
LEFT JOIN `tabPurchase Invoice Item` ON `tabGL Entry`.`voucher_no` = `tabPurchase Invoice Item`.`parent`
WHERE
  `tabGL Entry`.`posting_date` BETWEEN %(start_date)s AND %(end_date)s
  AND `tabGL Entry`.`account` LIKE %(account)s
  AND `tabGL Entry`.`company` = %(company)s
  AND `tabGL Entry`.`is_cancelled` = 0
ORDER BY
  `tabGL Entry`.`posting_date` ASC,
  `tabGL Entry`.`voucher_no` ASC,
  `tabGL Entry`.`name` ASC;
-------------------------------------------------


Filters:
Label		start_date
Fieldtype	Date
Fieldname	start_date
Mandatory	
Options	
	
	
Label		end_date
Fieldtype	Date
Fieldname	end_date
Mandatory	
Options	
	
	
Label		company
Fieldtype	Link
Fieldname	company
Mandatory	
Options		Company
	
	
Label		Account
Fieldtype	Link
Fieldname	account
Mandatory	
Options		Account
Dim dbsCurrent As Database, dbsContacts As Database 
Set dbsCurrent = CurrentDb 
Set dbsContacts = DBEngine.Workspaces(0).OpenDatabase("Contacts.mdb")
// Genereer een random veelvoud van 10 tussen min (inclusief) en max (exclusief)
37
function randomMultipleOf10(min, max) {
38
  let randomNumber = random(min / 10, max / 10);
39
  let roundedNumber = Math.floor(randomNumber);
40
  return roundedNumber * 10;
41
}
<style>
.content-hide {
    display: none;
}

.details-area {
    max-height: 70px;
    overflow: hidden;
    transition: max-height 0.5s ease;
}

.show-more, .show-less {
cursor: pointer;
}

</style>

<script type="text/javascript">
var $ = jQuery;
$(document).ready(function() {
    $('.show-more').on('click', function(event) {
        event.preventDefault();
        var profileCard = $(this).closest('.profile-card');
        var detailsArea = profileCard.find('.details-area');
        
        $(this).addClass('content-hide');
        profileCard.find('.show-less').removeClass('content-hide');

        detailsArea.css('max-height', detailsArea[0].scrollHeight + 'px'); // Rozwijanie
    });

    $('.show-less').on('click', function(event) {
        event.preventDefault();
        var profileCard = $(this).closest('.profile-card');
        var detailsArea = profileCard.find('.details-area');
        
        $(this).addClass('content-hide');
        profileCard.find('.show-more').removeClass('content-hide');
        
        detailsArea.css('max-height', '70px'); // Zwijanie
    });
});
</script>
    function showPreview(event){
  if(event.target.files.length > 0){
    var src = URL.createObjectURL(event.target.files[0]);
    var preview = document.getElementById("file-ip-1-preview");
    preview.src = src;
    preview.style.display = "block";
  }
}
//profilepage component
//html
<div class="container-fluid" style="margin-top: 60px;">
  <div class="row justify-content-start">
    <!-- Back Button -->
    <div class="col-6">
      <button class="btn btn-link" (click)="goBack()">
        <i class="bi bi-arrow-left-circle"></i> Back
      </button>
    </div>

    <!-- My Profile Heading -->
    <div class="col-6">
      <h2 class="text-left">My Profile</h2>
    </div>

    <!-- Left Side Menu -->
    <div class="col-md-3">
      <h5 class="small-heading">
        <i class="bi bi-bell-fill"></i>
        Notifications
      </h5>
      <!-- Notification placeholder -->
      <div class="card mt-3">
        <div class="card-body">
          <div *ngIf="unredeemedRewards.length === 0" class="notification-item">
            <span>No Notifications</span>
          </div>
          <div *ngFor="let reward of unredeemedRewards" class="notification-item">
            <span>{{ reward.reward_Type_Name }}</span>
            <button class="btn btn-sm btn-primary" (click)="openRedeemModal(reward)">Redeem</button>
          </div>
        </div>
      </div>
    </div>

    

    <!-- Vertical Line Separator -->
    <div class="col-md-1">
      <div class="vertical-line"></div>
    </div>

    <!-- Right Side Form -->
    <div class="col-md-6">
      <form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
        <h5 class="small-heading">
          <i class="bi bi-house-gear-fill"></i>
          Personal Details
        </h5>
        <div class="text-center mb-3">
          <div class="profile-photo-wrapper">
            <img [src]="userProfileImage" alt="Profile Photo" class="img-fluid rounded-circle profile-photo" *ngIf="userProfileImage">
            <div class="edit-photo-wrapper">
              <a href="#" (click)="enableEditMode($event)" class="edit-link">Edit</a>
              <label [class.disabled-icon]="!isEditMode" for="photoUpload" class="photo-upload-icon">
                <i class="bi bi-camera"></i>
              </label>
            </div>
            <input type="file" id="photoUpload" formControlName="photo" class="d-none" (change)="onPhotoChange($event)" [readonly]="!isEditMode" >
          </div>
        </div>
        

        <br>

      
        <div class="row">
          <div class="col-md-6 mb-3">
            <div class="form-group">
              <label for="name" class="form-label">Name</label>
              <input type="text" class="form-control" id="name" formControlName="name" [readonly]="!isEditMode" [ngClass]="{'disabled-input': !isEditMode}">
              <div *ngIf="profileForm.controls['name'].invalid && (profileForm.controls['name'].dirty || profileForm.controls['name'].touched)" class="alert">
                <div *ngIf="profileForm.controls['name'].errors?.['required']">Name is required.</div>
                <div *ngIf="profileForm.controls['name'].errors?.['minlength']">Name must be at least 3 characters long.</div>
                <div *ngIf="profileForm.controls['name'].errors?.['maxlength']">Name must be at most 50 characters long.</div>
              </div>
            </div>
          </div>

          <div class="col-md-6 mb-3">
            <div class="form-group">            
              <label for="surname" class="form-label">Surname</label>
              <input type="text" class="form-control" id="surname" formControlName="surname" [readonly]="!isEditMode" [ngClass]="{'disabled-input': !isEditMode}">
              <div *ngIf="profileForm.controls['surname'].invalid && (profileForm.controls['surname'].dirty || profileForm.controls['surname'].touched)" class="alert">
                <div *ngIf="profileForm.controls['surname'].errors?.['required']">Surname is required.</div>
                <div *ngIf="profileForm.controls['surname'].errors?.['minlength']">Surname must be at least 3 characters long.</div>
                <div *ngIf="profileForm.controls['surname'].errors?.['maxlength']">Surname must be at most 50 characters long.</div>
              </div>
            </div>
          </div>

          <div class="col-md-6 mb-3">
            <div class="form-group">
              <label for="email" class="form-label">Email Address</label>
              <input type="email" class="form-control" id="email" formControlName="email" [readonly]="!isEditMode" [ngClass]="{'disabled-input': !isEditMode}">
              <div *ngIf="profileForm.controls['email'].invalid && (profileForm.controls['email'].dirty || profileForm.controls['email'].touched)" class="alert">
                <div *ngIf="profileForm.controls['email'].errors?.['required']">Email is required.</div>
              </div>
            </div>
          </div>

          <div class="col-md-6 mb-3">
            <div class="form-group">
              <label for="phoneNumber" class="form-label">Contact Number</label>
              <input type="text" class="form-control" id="phoneNumber" formControlName="phoneNumber" [readonly]="!isEditMode" [ngClass]="{'disabled-input': !isEditMode}">
              <div *ngIf="profileForm.controls['phoneNumber'].invalid && (profileForm.controls['phoneNumber'].dirty || profileForm.controls['phoneNumber'].touched)" class="alert">
                <div *ngIf="profileForm.controls['phoneNumber'].errors?.['required']">Contact number is required.</div>
                <div *ngIf="profileForm.controls['phoneNumber'].errors?.['maxlength']">Contact number must be a valid 10-digit number.</div>
              </div>
            </div>
          </div>

          <div class="col-md-6 mb-3">
            <div class="form-group">
              <label for="physical_Address" class="form-label">Physical Address</label>
              <input type="text" class="form-control" id="physical_Address" formControlName="physical_Address" [readonly]="!isEditMode" [ngClass]="{'disabled-input': !isEditMode}">
              <div *ngIf="profileForm.controls['physical_Address'].invalid && (profileForm.controls['physical_Address'].dirty || profileForm.controls['physical_Address'].touched)" class="alert">
                <div *ngIf="profileForm.controls['physical_Address'].errors?.['required']">Physical address is required.</div>
                <div *ngIf="profileForm.controls['physical_Address'].errors?.['maxlength']">Physical address must be at most 255 characters long.</div>
              </div>
            </div>
          </div>
        </div>

        <div class="d-flex justify-content-end">
          <button type="button" class="btn btn-primary me-2" (click)="openSaveModal()" [disabled]="!isEditMode">Save</button>
          <button type="button" class="btn btn-info" (click)="changePassword()" [disabled]="!isEditMode">Change Password</button>
        </div>
      </form>
    </div>
  </div>
</div>

<!-- Save Confirmation Modal -->
<div class="modal fade" id="saveConfirmationModal" tabindex="-1" role="dialog" aria-labelledby="saveConfirmationModalTitle" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="saveConfirmationModalTitle">Save Changes</h5>
      </div>
      <div class="modal-body">
        Are you sure you want to update your profile details?
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" (click)="dismissModal()">Cancel</button>
        <button type="button" class="btn btn-primary" (click)="confirmSave()">Confirm</button>
      </div>
    </div>
  </div>
</div>

<!-- Error Modal -->
<div class="modal fade" id="errorModal" tabindex="-1" role="dialog" aria-labelledby="errorModalTitle" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="errorModalTitle">Error</h5>
      </div>
      <div class="modal-body">
        Please enter a valid input for the following fields:
        <ul id="errorList"></ul>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-primary" (click)="dismissErrorModal()">OK</button>
      </div>
    </div>
  </div>
</div>

<!-- Redeem Reward Modal -->
<div class="modal fade" id="redeemRewardModal" tabindex="-1" role="dialog" aria-labelledby="redeemRewardModalTitle" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="redeemRewardModalTitle">Redeem Reward</h5>
      </div>
      <div class="modal-body">
        Are you sure you want to redeem the reward {{ selectedReward?.reward_Type_Name }}?
        <p>{{ selectedReward?.reward_Criteria }}</p>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" (click)="dismissRedeemModal()">No</button>
        <button type="button" class="btn btn-primary" (click)="confirmRedeem()">Yes</button>
      </div>
    </div>
  </div>
</div>

<!-- Success Modal -->
<div class="modal fade" id="successModal" tabindex="-1" role="dialog" aria-labelledby="successModalTitle" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="successModalTitle">Success</h5>
        <button type="button" class="close" (click)="dismissSuccessModal()">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        Congratulations! You've successfully redeemed this reward {{ selectedReward?.reward_Type_Name }}. Please contact the gym for further instructions.
      </div>
    </div>
  </div>
</div>

//ts
import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormGroup, Validators, FormBuilder } from '@angular/forms';
import { UserService } from '../Services/userprofile.service';
import { Router } from '@angular/router';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms';
import { Member, updateUser } from '../shared/update-user';
import { Subscription, catchError } from 'rxjs';
import { RewardRedeemViewModel, UnredeemedRewardModel } from '../shared/reward';
import { RewardService } from '../Services/reward.service';
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';

declare var $: any; // Import jQuery

@Component({
  selector: 'app-profile-page',
  standalone: true,
  imports: [CommonModule, ReactiveFormsModule],
  templateUrl: './profile-page.component.html',
  styleUrls: ['./profile-page.component.css']
})
export class ProfilePageComponent implements OnInit, OnDestroy {
  profileForm: FormGroup;
  user: updateUser | undefined;
  member: Member | undefined;
  isEditMode = false;
  errorMessage = '';  
  userProfileImage: string | null = null;
  unredeemedRewards: UnredeemedRewardModel[] = [];
  selectedReward: UnredeemedRewardModel | null = null;

  private userSubscription: Subscription | undefined;
  private memberSubscription: Subscription | undefined;
  private redeemSubscription: Subscription | undefined;
  
  constructor(
    private userService: UserService,
    private rewardService: RewardService,
    private router: Router,
    private fb: FormBuilder
  ) {
    this.profileForm = this.fb.group({
      email: ['', [Validators.required, Validators.email]],
      name: ['', [Validators.required, Validators.minLength(3), Validators.maxLength(20)]],
      surname: ['', [Validators.required, Validators.minLength(3), Validators.maxLength(20)]],
      phoneNumber: ['', [Validators.required, Validators.maxLength(10)]],
      physical_Address: ['', [Validators.required, Validators.maxLength(255)]],
      photo: ['']
    });
  }

  ngOnInit(): void {
    const userId = JSON.parse(localStorage.getItem('User') || '{}').userId;
    console.log('User ID from local storage:', userId);
    this.loadUserProfile(userId);
    this.isEditMode = false;
  }

  ngOnDestroy(): void {
    // Clean up subscriptions
    if (this.userSubscription) {
      this.userSubscription.unsubscribe();
    }
    if (this.memberSubscription) {
      this.memberSubscription.unsubscribe();
    }
    if (this.redeemSubscription) {
      this.redeemSubscription.unsubscribe();
    }
  }

  loadUserProfile(userId: number): void {
    this.userSubscription = this.userService.getUserById(userId).pipe(
      catchError(error => {
        console.error('Error fetching user profile:', error);
        return [];
      })
    ).subscribe({
      next: (result) => {
        console.log('User data received:', result);
        this.user = result; 
        // Log photo to debug
        console.log('Photo:', this.user.photo);        
        // Set user profile image
        this.userProfileImage = `data:image/jpeg;base64,${this.user.photo}`;
        this.profileForm.patchValue(this.user);
        console.log('User:', this.user);

        if (this.user.user_Type_ID === 3) {
          this.loadMemberProfile(userId);
        }
      },
      complete: () => {
        console.log('getUserById subscription completed');
      }
    });
  }

  loadMemberProfile(userId: number): void {
    this.memberSubscription = this.userService.getMemberByUserId(userId).pipe(
      catchError(error => {
        console.error('Error fetching member profile:', error);
        return [];
      })
    ).subscribe({
      next: (result) => {
        this.member = result;
        if (this.member) {
          this.loadUnredeemedRewards(this.member.Member_ID);
        }
      },
      complete: () => {
        console.log('getMemberByUserId subscription completed');
      }
    });
  }

  clearForm() {
    this.profileForm.reset();
  }

  enableEditMode(event: Event) {
    event.preventDefault();
    this.isEditMode = true;
    this.profileForm.enable();
  }

  openSaveModal() {
    if (this.profileForm.invalid) {
      this.showValidationErrors();
      $('#errorModal').modal('show');
      return;
    }
    $('#saveConfirmationModal').modal('show');
  }

  showValidationErrors() {
    const invalidFields: string[] = [];
    Object.keys(this.profileForm.controls).forEach(key => {
      const controlErrors = this.profileForm.get(key)?.errors;
      if (controlErrors) {
        Object.keys(controlErrors).forEach(errorKey => {
          invalidFields.push(`${key}: ${errorKey}`);
        });
      }
    });
    this.errorMessage = `Please enter a valid input: ${invalidFields.join(', ')}`;
  }

  dismissModal() {
    $('#saveConfirmationModal').modal('hide');
  }

  dismissErrorModal() {
    $('#errorModal').modal('hide');
  }

  confirmSave() {
    this.dismissModal();
    this.onSubmit();
    this.isEditMode = false; // Disable edit mode after confirmation
  }

  onSubmit() {
    if (this.profileForm.valid) {
      const userId = JSON.parse(localStorage.getItem('User')!).userId;
      this.userService.updateUser(userId, this.profileForm.value).subscribe({
        next: (result) => {
          console.log('User to be updated:', result);
          this.router.navigateByUrl(`/ProfilePage/${userId}`);
          alert('Successfully updated profile');
        },
        error: () => {
          console.error('Error updating user profile:');
          alert('Error updating profile');
        }
      });
    }
  }

  onPhotoChange(event: Event): void {
    if (!this.isEditMode) return;

    const input = event.target as HTMLInputElement;
    if (input.files && input.files[0]) {
      const reader = new FileReader();
      reader.onload = (e: any) => {
        this.userProfileImage = e.target.result; // Update the image source
        this.profileForm.patchValue({ photo: e.target.result }); // Update the form control
      };
      reader.readAsDataURL(input.files[0]);
    }
  }

  goBack() {
    const userTypeId = JSON.parse(localStorage.getItem('User')!).userTypeId;
    const userId = JSON.parse(localStorage.getItem('User')!).userId;
    if (userTypeId === 1) {  // Ensure userTypeID is compared as string
      this.router.navigateByUrl(`/OwnerHome/${userId}`);
    } else if (userTypeId === 2) {
      this.router.navigateByUrl(`/EmployeeHome/${userId}`);
    } else if (userTypeId === 3) {
      this.router.navigateByUrl(`/Home/${userId}`);
    }
  }

  changePassword() {
    this.router.navigateByUrl('/ChangePasswordPage');
  }

  // Method to load rewards for the current user
  loadUnredeemedRewards(memberId: number): void {
    this.rewardService.getUnredeemedRewardsForMember(memberId).subscribe(
      rewards => {
        this.unredeemedRewards = rewards;
      },
      error => {
        console.error('Error fetching unredeemed rewards:', error);
      }
    );
  }

  // Method to open redeem modal for a reward
  openRedeemModal(reward: UnredeemedRewardModel): void {
    this.selectedReward = reward;
    $('#redeemRewardModal').modal('show');
  }

  // Method to dismiss redeem modal
  dismissRedeemModal(): void {
    $('#redeemRewardModal').modal('hide');
  }

  // Method to confirm redeeming a reward
  confirmRedeem(): void {
    if (!this.selectedReward) {
      return;
    }
    const redeemRequest = new RewardRedeemViewModel();
    redeemRequest.MemberId = this.member?.Member_ID ?? 0;
    redeemRequest.RewardId = this.selectedReward.reward_ID;

    // Call backend service to redeem the reward
    this.redeemSubscription = this.rewardService.redeemReward(redeemRequest).subscribe({
      next: () => {
        // Show success modal on successful redemption
        $('#successModal').modal('show');
        // Remove redeemed reward from the list
        this.unredeemedRewards = this.unredeemedRewards.filter(r => r.reward_ID !== this.selectedReward?.reward_ID);
      },
      error: (error) => {
        console.error('Error redeeming reward:', error);
        // Handle error
      }
    });
    this.dismissRedeemModal();
  }

  // Method to dismiss success modal
  dismissSuccessModal(): void {
    $('#successModal').modal('hide');
  }
}


//models
export class updateUser
{
   name!: string;
   surname!: string;
   email!: string;
   physical_Address!: string;
   phoneNumber!: string;
   photo!: string;
   user_Type_ID!: number;
}
export class UserProfile
{   
   User_ID !: number
   Id!: number
   Name!: string
   Surname!: string
   ID_Number!: string
   Email!: string
   Physical_Address!: string
   PhoneNumber!: string
   Photo!: string
   UserName!: string
   PasswordHash!: string
   Date_of_Birth!: Date
   User_Status_ID !: number
   User_Type_ID!: number
}

//service
import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, catchError, map, throwError } from 'rxjs';
import { UserProfile } from '../Models/UserProfile';
import { LoginUser } from '../shared/login-user';
import { User } from '../shared/user';
import { updateUser } from '../shared/update-user';
import { UserTypeViewModel } from '../shared/user-type-vm';
import { UserViewModel } from '../shared/search-user';

@Injectable({
  providedIn: 'root'
})
export class UserService {

  httpOptions = {
    headers: new HttpHeaders({
      'Content-Type': 'application/json'
    })
  };

  constructor(private http: HttpClient) {}
  endPoint: string = "https://localhost:7185/api/";

  //User EndPoints
  // RegisterUser(registerUser: RegisterUser) {
  //   return this.http.post(`${this.endPoint}User/Register`, registerUser, this.httpOptions);
  // }

  RegisterUser(formData: FormData): Observable<any> {
    return this.http.post(`${this.endPoint}User/Register`, formData);
  }

  
  LoginUser(loginUser: LoginUser): Observable<User> {
    return this.http.post<User>(`${this.endPoint}User/Login`, loginUser, this.httpOptions);
  }

  getAllUsers(): Observable<UserProfile[]> {
    return this.http.get<UserProfile[]>(this.endPoint + "User/getAllUsers");
  }

  getUserById(userId: number): Observable<updateUser> {
    return this.http.get<updateUser>(`${this.endPoint}User/getUserById/${userId}`)
    .pipe(map(result => result))
  }

  getMemberByUserId(userId: number): Observable<any> {
    return this.http.get<any>(`${this.endPoint}User/GetMemberByUserId/${userId}`, this.httpOptions);
  }

  updateUser(userId: string, user: updateUser): Observable<any> {
    return this.http.put(`${this.endPoint}User/editUser/${userId}`, user, { responseType: 'text' })
    .pipe(
      map((response: string) => {
        try {
          // Attempt to parse as JSON if the response is in JSON format
          return JSON.parse(response);
        } catch {
          // Return as plain text if not JSON
          return response;
        }
      }),
      catchError(this.handleError)
    );
  }

  private handleError(error: HttpErrorResponse) {
    if (error.error instanceof ErrorEvent) {
      console.error('An error occurred:', error.error.message);
    } else {
      console.error(
        `Backend returned code ${error.status}, ` +
        `body was: ${error.error}`);
    }
    return throwError(() => new Error('Something bad happened; please try again later.'))    
  }
  
  searchUsers(criteria: string): Observable<UserViewModel[]> {
    return this.http.get<UserViewModel[]>(`${this.endPoint}User/SearchUsers?criteria=${criteria}`);
  }

  deleteUser(userId: number): Observable<string> {
    return this.http.delete<string>(`${this.endPoint}User/deleteUser/${userId}`);
  }

  //UserType EndPoints
  addUserType(userType: UserTypeViewModel): Observable<UserTypeViewModel> {
    return this.http.post<UserTypeViewModel>(`${this.endPoint}UserType/addUserType`, userType, this.httpOptions);
  }

  getAllUserTypes(): Observable<UserTypeViewModel[]> {
    return this.http.get<UserTypeViewModel[]>(`${this.endPoint}UserType/getAllUserTypes`, this.httpOptions);
  }

  getUserTypeById(id: number): Observable<UserTypeViewModel> {
    return this.http.get<UserTypeViewModel>(`${this.endPoint}UserType/getUserTypeById/${id}`, this.httpOptions);
  }

  updateUserType(userTypeId: number, userTypeName: string): Observable<void> {
    const body = { user_Type_Name: userTypeName }; // Correctly format the body as JSON
    return this.http.put<void>(`${this.endPoint}UserType/updateUserType/${userTypeId}`, body, this.httpOptions);
  }

  deleteUserType(id: number): Observable<void> {
    return this.http.delete<void>(`${this.endPoint}UserType/deleteUserType/${id}`, this.httpOptions);
  }
}
selector:hover .elementor-heading-title {
     color: #F9F2E8;
     transition-duration: 300ms
}
<a href="https://maticz.com/ecommerce-app-development">Ecommerce App Development</a>
function clearRow() {
  
  var ss = SpreadsheetApp.getActiveSpreadsheet();  
  var editSheet = ss.getSheetByName("EDIT"); 
  var lastRowEdit = editSheet.getLastRow();
  
  for(var i = 2; i <= lastRowEdit; i++)
  {  
   
   if(editSheet.getRange(i,1).getValue() == 'TRAIN')
   {
     editSheet.getRange('A' + i + ':C' + i).clear();     
   }    
  }    
}

function deleteRow() {
  
  var ss = SpreadsheetApp.getActiveSpreadsheet(); 
  var editSheet = ss.getSheetByName("EDIT"); 
  var lastRowEdit = editSheet.getLastRow();
  
  for(var i = 2; i <= lastRowEdit; i++)
  {  
   
   if(editSheet.getRange(i,1).getValue() == 'TRAIN')
   {
     editSheet.deleteRow(i);    
   }
    
  }    
}

function insertRow() {
  
  var ss = SpreadsheetApp.getActiveSpreadsheet(); 
  var editSheet = ss.getSheetByName("EDIT"); 
  var lastRowEdit = editSheet.getLastRow();
  
  for(var i = 2; i <= lastRowEdit; i++)
  {  
   
   if(editSheet.getRange(i,1).getValue() == 'TRAIN')
   {
     editSheet.insertRowAfter(i);    
   }
    
  }   
}

function replaceRow() {
  
  var ss = SpreadsheetApp.getActiveSpreadsheet();  
  var editSheet = ss.getSheetByName("EDIT"); 
  var lastRowEdit = editSheet.getLastRow();
  
  for(var i = 2; i <= lastRowEdit; i++)
  {  
   
   if(editSheet.getRange(i,1).getValue() == 'TRAIN')
   {
     editSheet.getRange('A' + i + ':C' + i).setValues([['AIRPLANE', 'ORANGE', 30]]);     
   }    
  }   
}

https://codewithcurt.com/how-to-clear-delete-insert-and-replace-row-using-google-apps-script/
<a href="https://maticz.com/insurance-software-development">Insurance Software Development</a>
SHOW search_path;
SET search_path TO Schema1, public;
star

Sun Jul 14 2024 11:13:35 GMT+0000 (Coordinated Universal Time)

@roamtravel

star

Sun Jul 14 2024 07:55:58 GMT+0000 (Coordinated Universal Time) https://youtu.be/WyY2Af3k1xI

@vishnu_jha #c++ #dsa #recursion #string #palindrome #checkpalindrome

star

Sun Jul 14 2024 07:40:35 GMT+0000 (Coordinated Universal Time) https://android.googlesource.com/platform/system/core/+/refs/heads/main/fastboot/device/commands.cpp

@Dewaldt

star

Sun Jul 14 2024 07:39:10 GMT+0000 (Coordinated Universal Time) https://android.googlesource.com/platform/system/core/+/refs/heads/main/fastboot/device/

@Dewaldt

star

Sun Jul 14 2024 07:37:41 GMT+0000 (Coordinated Universal Time) https://android.googlesource.com/platform/system/core/+/master/fastboot/

@Dewaldt

star

Sun Jul 14 2024 07:37:01 GMT+0000 (Coordinated Universal Time) https://android.googlesource.com/platform/system/core/+/master/fastboot/

@Dewaldt

star

Sun Jul 14 2024 07:36:26 GMT+0000 (Coordinated Universal Time) https://android.googlesource.com/platform/system/core/+/master/fastboot/#fastboot

@Dewaldt

star

Sun Jul 14 2024 07:35:57 GMT+0000 (Coordinated Universal Time) https://android.googlesource.com/platform/system/core/+/master/fastboot/

@Dewaldt

star

Sun Jul 14 2024 07:34:28 GMT+0000 (Coordinated Universal Time) https://android.googlesource.com/platform/system/core/+/master/fastboot/

@Dewaldt

star

Sun Jul 14 2024 07:33:43 GMT+0000 (Coordinated Universal Time) https://android.googlesource.com/platform/system/core/+/master/fastboot/

@Dewaldt

star

Sun Jul 14 2024 07:32:47 GMT+0000 (Coordinated Universal Time) https://android.googlesource.com/platform/system/core/+/master/fastboot/#fastboot

@Dewaldt

star

Sun Jul 14 2024 07:31:53 GMT+0000 (Coordinated Universal Time) https://android.googlesource.com/platform/system/core/+/master/fastboot/#fastboot

@Dewaldt

star

Sun Jul 14 2024 07:31:35 GMT+0000 (Coordinated Universal Time) https://android.googlesource.com/platform/system/core/+/master/fastboot/#fastboot

@Dewaldt

star

Sun Jul 14 2024 07:31:20 GMT+0000 (Coordinated Universal Time) https://android.googlesource.com/platform/system/core/+/master/fastboot/#fastboot

@Dewaldt

star

Sun Jul 14 2024 07:31:10 GMT+0000 (Coordinated Universal Time) https://android.googlesource.com/platform/system/core/+/master/fastboot/#fastboot

@Dewaldt

star

Sun Jul 14 2024 07:30:20 GMT+0000 (Coordinated Universal Time) https://android.googlesource.com/platform/system/core/+/master/fastboot/#fastboot

@Dewaldt

star

Sun Jul 14 2024 07:30:09 GMT+0000 (Coordinated Universal Time) https://android.googlesource.com/platform/system/core/+/master/fastboot/#fastboot

@Dewaldt

star

Sun Jul 14 2024 07:29:57 GMT+0000 (Coordinated Universal Time) https://android.googlesource.com/platform/system/core/+/master/fastboot/#fastboot

@Dewaldt

star

Sun Jul 14 2024 07:29:14 GMT+0000 (Coordinated Universal Time) https://android.googlesource.com/platform/system/core/+/master/fastboot/#fastboot

@Dewaldt

star

Sun Jul 14 2024 07:21:22 GMT+0000 (Coordinated Universal Time) https://android.googlesource.com/platform/system/core/+/refs/heads/main/fastboot/usb_linux.cpp

@Dewaldt

star

Sun Jul 14 2024 07:15:35 GMT+0000 (Coordinated Universal Time) https://android.googlesource.com/platform/system/core/+/master/fastboot/

@Dewaldt

star

Sun Jul 14 2024 07:12:37 GMT+0000 (Coordinated Universal Time) https://android.googlesource.com/platform/system/core/+/refs/heads/main/fastboot/util.h

@Dewaldt

star

Sun Jul 14 2024 05:43:20 GMT+0000 (Coordinated Universal Time)

@jrray #python

star

Sat Jul 13 2024 22:50:15 GMT+0000 (Coordinated Universal Time)

@webisko #php

star

Sat Jul 13 2024 22:19:01 GMT+0000 (Coordinated Universal Time)

@davidmchale #ternary #nested #function

star

Sat Jul 13 2024 16:57:43 GMT+0000 (Coordinated Universal Time) https://limpanomecpf.framer.website/

@danzcampos

star

Sat Jul 13 2024 16:56:26 GMT+0000 (Coordinated Universal Time) https://www.mercadolivre.com.br/

@danzcampos

star

Sat Jul 13 2024 14:10:15 GMT+0000 (Coordinated Universal Time) https://learnbricksbuilder.com/build-a-custom-wordpress-dashboard-with-bricks-udp/

@webisko #php

star

Sat Jul 13 2024 11:08:00 GMT+0000 (Coordinated Universal Time)

@mebean #flutter #laravel #api #http

star

Sat Jul 13 2024 09:38:01 GMT+0000 (Coordinated Universal Time) https://medium.com/analytics-vidhya/getting-started-with-brain-js-39748358aea2

@ZCOMStudios

star

Sat Jul 13 2024 09:37:49 GMT+0000 (Coordinated Universal Time) https://medium.com/analytics-vidhya/getting-started-with-brain-js-39748358aea2

@ZCOMStudios #javascript #brainjs #bash #npm #nodejs

star

Sat Jul 13 2024 08:38:40 GMT+0000 (Coordinated Universal Time)

@hd310

star

Sat Jul 13 2024 08:09:33 GMT+0000 (Coordinated Universal Time) https://tapitra.com/

@outemux

star

Sat Jul 13 2024 06:01:37 GMT+0000 (Coordinated Universal Time)

@mry2khuu #c# #ability #attack

star

Sat Jul 13 2024 00:07:43 GMT+0000 (Coordinated Universal Time)

@roamtravel

star

Fri Jul 12 2024 19:00:10 GMT+0000 (Coordinated Universal Time)

@jrg_300i #undefined

star

Fri Jul 12 2024 18:19:24 GMT+0000 (Coordinated Universal Time)

@Taimoor

star

Fri Jul 12 2024 18:18:25 GMT+0000 (Coordinated Universal Time)

@Taimoor

star

Fri Jul 12 2024 18:17:40 GMT+0000 (Coordinated Universal Time)

@Taimoor

star

Fri Jul 12 2024 18:15:51 GMT+0000 (Coordinated Universal Time)

@Taimoor

star

Fri Jul 12 2024 16:27:39 GMT+0000 (Coordinated Universal Time) https://learn.microsoft.com/en-us/office/vba/api/access.application.currentdb

@rmdnhsn

star

Fri Jul 12 2024 15:55:12 GMT+0000 (Coordinated Universal Time) https://editor.p5js.org/seb_prjcts.be/sketches/dgoadQ2h8

@seb_prjcts_be

star

Fri Jul 12 2024 15:26:52 GMT+0000 (Coordinated Universal Time)

@webisko #javascript

star

Fri Jul 12 2024 15:15:14 GMT+0000 (Coordinated Universal Time)

@alamin005 #javascript

star

Fri Jul 12 2024 15:13:48 GMT+0000 (Coordinated Universal Time)

@iamkatmakhafola

star

Fri Jul 12 2024 13:25:49 GMT+0000 (Coordinated Universal Time)

@webisko #css

star

Fri Jul 12 2024 13:05:14 GMT+0000 (Coordinated Universal Time) https://maticz.com/ecommerce-app-development

@Ameliasebastian #ecommerceappdevelopmentcompany

star

Fri Jul 12 2024 13:01:33 GMT+0000 (Coordinated Universal Time)

@roamtravel

star

Fri Jul 12 2024 12:40:28 GMT+0000 (Coordinated Universal Time) https://maticz.com/insurance-software-development

@Ameliasebastian #insurancesoftwaredevelopment

star

Fri Jul 12 2024 11:02:41 GMT+0000 (Coordinated Universal Time)

@ClemensBerteld #postgres #postgis #sql

Save snippets that work with our extensions

Available in the Chrome Web Store Get Firefox Add-on Get VS Code extension