/****************************************************************************** * * Copyright 2022 Google LLC * * 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 #include #include #include namespace fs = std::filesystem; // The user data should be stored in the subdirectory of |USER_DE_PATH| static const std::string USER_DE_PATH = "/data/user_de/0"; // The migration process start only if |MIGRATION_FILE_CHECKER| is found in a // previous location static const std::string MIGRATION_FILE_CHECKER = "databases/bluetooth_db"; // List of possible package_name for bluetooth to get the data from / to static const std::vector ALLOWED_BT_PACKAGE_NAME = { "com.android.bluetooth", // legacy name "com.android.bluetooth.services", // Beta users "com.google.android.bluetooth.services", // Droid fooder users }; // Accessor to get the default allowed package list to be used in migration // OEM can call their own method with their own allowed list const std::vector get_allowed_bt_package_name(void) { return ALLOWED_BT_PACKAGE_NAME; } // Check if |dst| is in |base_dir| subdirectory and check the package name in // |dst| is a allowed package name in the |pkg_list| // // Return an empty string if an issue occurred // or the package name contained in |dst| on success static std::string parse_destination_package_name( const std::string& dst, const std::string& base_dir, const std::vector& pkg_list) { const std::size_t found = dst.rfind("/"); // |dst| must contain a '/' if (found == std::string::npos) { LOG(ERROR) << "Destination format not valid " << dst; return ""; } // |dst| directory is supposed to be in |base_dir| if (found != base_dir.length()) { LOG(ERROR) << "Destination location not allowed: " << dst; return ""; } // This check prevent a '/' to be at the end of |dst| if (found >= dst.length() - 1) { LOG(ERROR) << "Destination format not valid " << dst; return ""; } const std::string dst_package_name = dst.substr(found + 1); // +1 for '/' if (std::find(pkg_list.begin(), pkg_list.end(), dst_package_name) == pkg_list.end()) { LOG(ERROR) << "Destination package_name not valid: " << dst_package_name << " Created from " << dst; return ""; } LOG(INFO) << "Current Bluetooth package name is: " << dst_package_name; return dst_package_name; } // Check for data to migrate from the |allowed_bt_package_name| // A migration will be performed if: // * |dst| is different than |allowed_bt_package_name| // * the following file is found: // |USER_DE_PATH|/|allowed_bt_package_name|/|MIGRATION_FILE_CHECKER| // // After migration occurred, the |MIGRATION_FILE_CHECKER| is deleted to ensure // the migration is only performed once void handle_migration(const std::string& dst, const std::vector& allowed_bt_package_name) { const std::string dst_package_name = parse_destination_package_name( dst, USER_DE_PATH, allowed_bt_package_name); if (dst_package_name.empty()) return; for (const auto& pkg_name : allowed_bt_package_name) { std::error_code error; if (dst_package_name == pkg_name) { LOG(INFO) << "Same location skipped: " << dst_package_name; continue; } const fs::path dst_path = dst; const fs::path pkg_path = USER_DE_PATH + "/" + pkg_name; const fs::path local_migration_file_checker = pkg_path.string() + "/" + MIGRATION_FILE_CHECKER; if (!fs::exists(local_migration_file_checker, error)) { LOG(INFO) << "Not a valid candidate for migration: " << pkg_path; continue; } const fs::copy_options copy_flag = fs::copy_options::overwrite_existing | fs::copy_options::recursive; fs::copy(pkg_path, dst_path, copy_flag, error); if (error) { LOG(ERROR) << "Migration failed: " << error.message(); } else { fs::remove(local_migration_file_checker); LOG(INFO) << "Migration completed from " << pkg_path << " to " << dst; } break; // Copy from one and only one directory } }