/* * 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. */ package com.google.android.libraries.mobiledatadownload.file.backends; import android.accounts.Account; import com.google.android.libraries.mobiledatadownload.file.common.internal.Preconditions; /** Helper for Uri classes to serialize Android accounts. */ public final class AccountSerialization { private static final String SHARED_ACCOUNT_STR = "shared"; /** A common {@link Account} with no associated user; it appears as "shared" on the filesystem. */ public static final Account SHARED_ACCOUNT = new Account(SHARED_ACCOUNT_STR, "mobstore"); /** * Validates and serializes an {@link Account} into a string representation ":", with * the exception of {@link #SHARED_ACCOUNT} which is serialized as {@link #SHARED_ACCOUNT_STR}. * *

The account will be URL encoded in its URI representation (so, eg, "@gmail.com" * will appear as "you%40gmail.com"), but not in the file path representation used to access disk. * {@link #SHARED_ACCOUNT} shows up as "shared" on the filesystem. * *

This method performs some account validation. Android Account itself requires that both the * type and name fields be present. In addition to this requirement, this method requires that the * type contain no colons (as these are the delimiter used internally for the account * serialization), and that neither the type nor the name include any slashes (as these are file * separators). * *

Note the Linux filesystem accepts filenames composed of any bytes except "/" and NULL. * * @throws IllegalArgumentException if the account does not conform to the specifications above */ public static String serialize(Account account) { validate(account); if (isSharedAccount(account)) { return SHARED_ACCOUNT_STR; } return account.type + ":" + account.name; } /** * Parses an account string generated by {@link #serialize} back into an {@link Account}, or * throws {@link IllegalArgumentException} if {@code accountStr} has an unrecognized format. */ public static Account deserialize(String accountStr) { if (isSharedAccount(accountStr)) { return SHARED_ACCOUNT; } int colonIdx = accountStr.indexOf(':'); Preconditions.checkArgument(colonIdx > -1, "Malformed account"); String type = accountStr.substring(0, colonIdx); String name = accountStr.substring(colonIdx + 1); return new Account(name, type); } static boolean isSharedAccount(String accountStr) { return SHARED_ACCOUNT_STR.equals(accountStr); } static boolean isSharedAccount(Account account) { return SHARED_ACCOUNT.equals(account); } private static void validate(Account account) { // Android Account already validates that name and type are not empty. Preconditions.checkArgument(account.type.indexOf(':') == -1, "Account type contains ':'."); Preconditions.checkArgument(account.type.indexOf('/') == -1, "Account type contains '/'."); Preconditions.checkArgument(account.name.indexOf('/') == -1, "Account name contains '/'."); } private AccountSerialization() {} }