blob: e8abb8affa6d615910093379807871cfebfdb0d5 [file] [log] [blame]
Santos Cordon176ae282014-07-14 02:02:14 -07001/*
2 * Copyright (C) 2014 The Android Open Source Project
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
17package com.android.telecomm;
18
19import android.content.ComponentName;
20import android.content.Context;
21
22import android.content.SharedPreferences;
23import android.content.SharedPreferences.Editor;
24import android.net.Uri;
25import android.provider.Settings;
26import android.telecomm.PhoneAccount;
27
28import java.util.ArrayList;
29import java.util.List;
30import java.util.Objects;
31
32/**
33 * Handles writing and reading PhoneAccount registration entries.
34 * TODO(santoscordon): Replace this implementation with a proper database stored in a Telecomm
35 * provider.
36 */
37final class PhoneAccountRegistrar {
38 private static final int VERSION = 1;
39 private static final String TELECOMM_PREFERENCES = "telecomm_prefs";
40 private static final String PREFERENCE_PHONE_ACCOUNTS = "phone_accounts";
41
42 private final Context mContext;
43
44 private final class DeserializationToken {
45 int currentIndex = 0;
46 final String source;
47
48 DeserializationToken(String source) {
49 this.source = source;
50 }
51 }
52
53 PhoneAccountRegistrar(Context context) {
54 mContext = context;
55 }
56
57 /**
58 * Adds a new phone account entry or updates an existing one.
59 */
60 boolean addAccount(PhoneAccount account) {
61 List<PhoneAccount> allAccounts = getAllAccounts();
62 // Should we implement an artificial limit for # of accounts associated with a single
63 // ComponentName?
64 allAccounts.add(account);
65
66 // Search for duplicates and remove any that are found.
67 for (int i = 0; i < allAccounts.size() - 1; i++) {
68 if (account.equalsComponentAndId(allAccounts.get(i))) {
69 // replace existing entry.
70 allAccounts.remove(i);
71 break;
72 }
73 }
74
75 return writeAllAccounts(allAccounts);
76 }
77
78 /**
79 * Removes an existing phone account entry.
80 */
81 boolean removeAccount(PhoneAccount account) {
82 List<PhoneAccount> allAccounts = getAllAccounts();
83
84 for (int i = 0; i < allAccounts.size(); i++) {
85 if (account.equalsComponentAndId(allAccounts.get(i))) {
86 allAccounts.remove(i);
87 return writeAllAccounts(allAccounts);
88 }
89 }
90
91 return false;
92 }
93
94 /**
95 * Returns a list of all accounts which the user has enabled.
96 */
97 List<PhoneAccount> getEnabledAccounts() {
98 List<PhoneAccount> allAccounts = getAllAccounts();
99 // TODO: filter list
100 return allAccounts;
101 }
102
103 /**
104 * Returns the list of all accounts registered with the system, whether or not the user
105 * has explicitly enabled them.
106 */
107 List<PhoneAccount> getAllAccounts() {
108 String value = getPreferences().getString(PREFERENCE_PHONE_ACCOUNTS, null);
109 return deserializeAllAccounts(value);
110 }
111
112 /**
113 * Returns the registered version of the account matching the component name and ID of the
114 * specified account.
115 */
116 PhoneAccount getRegisteredAccount(PhoneAccount account) {
117 for (PhoneAccount registeredAccount : getAllAccounts()) {
118 if (registeredAccount.equalsComponentAndId(account)) {
119 return registeredAccount;
120 }
121 }
122 return null;
123 }
124
125 /**
126 * Replaces the contents of our list of accounts with this new list.
127 */
128 private boolean writeAllAccounts(List<PhoneAccount> allAccounts) {
129 Editor editor = getPreferences().edit();
130 editor.putString(PREFERENCE_PHONE_ACCOUNTS, serializeAllAccounts(allAccounts));
131 return editor.commit();
132 }
133
134 // Serialization implementation
135 // Serializes all strings into the format "len:string-value"
136 // Example, we will serialize the following PhoneAccount.
137 // PhoneAccount
138 // ComponentName: "abc"
139 // Id: "def"
140 // Handle: "555"
141 // Capabilities: 1
142 //
143 // Each value serializes into (spaces added for readability)
144 // 3:abc 3:def 3:555 1:1
145 //
146 // Two identical accounts would likewise be serialized as a list of strings with a prepended
147 // size of 2.
148 // 1:2 3:abc 3:def 3:555 1:1 3:abc 3:def 3:555 1:1
149 //
150 // The final result with a prepended version ("1:1") would be:
151 // "1:11:23:abc3:def3:5551:13:abc3:def3:5551:1"
152
153 private String serializeAllAccounts(List<PhoneAccount> allAccounts) {
154 StringBuilder buffer = new StringBuilder();
155
156 // Version
157 serializeIntValue(VERSION, buffer);
158
159 // Number of accounts
160 serializeIntValue(allAccounts.size(), buffer);
161
162 // The actual accounts
163 for (int i = 0; i < allAccounts.size(); i++) {
164 PhoneAccount account = allAccounts.get(i);
165 serializeStringValue(account.getComponentName().flattenToShortString(), buffer);
166 serializeStringValue(account.getId(), buffer);
167 serializeStringValue(account.getHandle().toString(), buffer);
168 serializeIntValue(account.getCapabilities(), buffer);
169 }
170
171 return buffer.toString();
172 }
173
174 private List<PhoneAccount> deserializeAllAccounts(String source) {
175 List<PhoneAccount> accounts = new ArrayList<PhoneAccount>();
176
177 if (source != null) {
178 DeserializationToken token = new DeserializationToken(source);
179 int version = deserializeIntValue(token);
180 if (version == 1) {
181 int size = deserializeIntValue(token);
182
183 for (int i = 0; i < size; i++) {
184 String strComponentName = deserializeStringValue(token);
185 String strId = deserializeStringValue(token);
186 String strHandle = deserializeStringValue(token);
187 int capabilities = deserializeIntValue(token);
188
189 accounts.add(new PhoneAccount(
190 ComponentName.unflattenFromString(strComponentName),
191 strId,
192 Uri.parse(strHandle),
193 capabilities));
194 }
195 }
196 }
197
198 return accounts;
199 }
200
201 private void serializeIntValue(int value, StringBuilder buffer) {
202 serializeStringValue(String.valueOf(value), buffer);
203 }
204
205 private void serializeStringValue(String value, StringBuilder buffer) {
206 buffer.append(value.length()).append(":").append(value);
207 }
208
209 private int deserializeIntValue(DeserializationToken token) {
210 return Integer.parseInt(deserializeStringValue(token));
211 }
212
213 private String deserializeStringValue(DeserializationToken token) {
214 int colonIndex = token.source.indexOf(':', token.currentIndex);
215 int valueLength = Integer.parseInt(token.source.substring(token.currentIndex, colonIndex));
216 int endIndex = colonIndex + 1 + valueLength;
217 token.currentIndex = endIndex;
218 return token.source.substring(colonIndex + 1, endIndex);
219 }
220
221 private SharedPreferences getPreferences() {
222 return mContext.getSharedPreferences(TELECOMM_PREFERENCES, Context.MODE_PRIVATE);
223 }
224}