blob: 8566dde9f40629e8fdaf2271708cf5fd862eb328 [file] [log] [blame]
/*
* 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 "<type>:<name>", with
* the exception of {@link #SHARED_ACCOUNT} which is serialized as {@link #SHARED_ACCOUNT_STR}.
*
* <p>The account will be URL encoded in its URI representation (so, eg, "<internal>@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.
*
* <p>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).
*
* <p>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() {}
}