• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 Google LLC
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.android.libraries.mobiledatadownload.file.backends;
17 
18 import android.accounts.Account;
19 import com.google.android.libraries.mobiledatadownload.file.common.internal.Preconditions;
20 
21 /** Helper for Uri classes to serialize Android accounts. */
22 public final class AccountSerialization {
23 
24   private static final String SHARED_ACCOUNT_STR = "shared";
25 
26   /** A common {@link Account} with no associated user; it appears as "shared" on the filesystem. */
27   public static final Account SHARED_ACCOUNT = new Account(SHARED_ACCOUNT_STR, "mobstore");
28 
29   /**
30    * Validates and serializes an {@link Account} into a string representation "<type>:<name>", with
31    * the exception of {@link #SHARED_ACCOUNT} which is serialized as {@link #SHARED_ACCOUNT_STR}.
32    *
33    * <p>The account will be URL encoded in its URI representation (so, eg, "<internal>@gmail.com"
34    * will appear as "you%40gmail.com"), but not in the file path representation used to access disk.
35    * {@link #SHARED_ACCOUNT} shows up as "shared" on the filesystem.
36    *
37    * <p>This method performs some account validation. Android Account itself requires that both the
38    * type and name fields be present. In addition to this requirement, this method requires that the
39    * type contain no colons (as these are the delimiter used internally for the account
40    * serialization), and that neither the type nor the name include any slashes (as these are file
41    * separators).
42    *
43    * <p>Note the Linux filesystem accepts filenames composed of any bytes except "/" and NULL.
44    *
45    * @throws IllegalArgumentException if the account does not conform to the specifications above
46    */
serialize(Account account)47   public static String serialize(Account account) {
48     validate(account);
49     if (isSharedAccount(account)) {
50       return SHARED_ACCOUNT_STR;
51     }
52     return account.type + ":" + account.name;
53   }
54 
55   /**
56    * Parses an account string generated by {@link #serialize} back into an {@link Account}, or
57    * throws {@link IllegalArgumentException} if {@code accountStr} has an unrecognized format.
58    */
deserialize(String accountStr)59   public static Account deserialize(String accountStr) {
60     if (isSharedAccount(accountStr)) {
61       return SHARED_ACCOUNT;
62     }
63     int colonIdx = accountStr.indexOf(':');
64     Preconditions.checkArgument(colonIdx > -1, "Malformed account");
65     String type = accountStr.substring(0, colonIdx);
66     String name = accountStr.substring(colonIdx + 1);
67     return new Account(name, type);
68   }
69 
isSharedAccount(String accountStr)70   static boolean isSharedAccount(String accountStr) {
71     return SHARED_ACCOUNT_STR.equals(accountStr);
72   }
73 
isSharedAccount(Account account)74   static boolean isSharedAccount(Account account) {
75     return SHARED_ACCOUNT.equals(account);
76   }
77 
validate(Account account)78   private static void validate(Account account) {
79     // Android Account already validates that name and type are not empty.
80     Preconditions.checkArgument(account.type.indexOf(':') == -1, "Account type contains ':'.");
81     Preconditions.checkArgument(account.type.indexOf('/') == -1, "Account type contains '/'.");
82     Preconditions.checkArgument(account.name.indexOf('/') == -1, "Account name contains '/'.");
83   }
84 
AccountSerialization()85   private AccountSerialization() {}
86 }
87