Add PhoneAccount registration.
Writes/Reads PhoneAccount registration as they are added and
removed through the TelecommManager APIs.
Ultimately, we may want to use a proper Telecomm-provider DB instead of
string-based serialized setting.
Bug: 16292368
Change-Id: I1214fcdd8728cddc949945a590b20e328de5ee7f
diff --git a/src/com/android/telecomm/PhoneAccountRegistrar.java b/src/com/android/telecomm/PhoneAccountRegistrar.java
new file mode 100644
index 0000000..e8abb8a
--- /dev/null
+++ b/src/com/android/telecomm/PhoneAccountRegistrar.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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.android.telecomm;
+
+import android.content.ComponentName;
+import android.content.Context;
+
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.net.Uri;
+import android.provider.Settings;
+import android.telecomm.PhoneAccount;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Handles writing and reading PhoneAccount registration entries.
+ * TODO(santoscordon): Replace this implementation with a proper database stored in a Telecomm
+ * provider.
+ */
+final class PhoneAccountRegistrar {
+ private static final int VERSION = 1;
+ private static final String TELECOMM_PREFERENCES = "telecomm_prefs";
+ private static final String PREFERENCE_PHONE_ACCOUNTS = "phone_accounts";
+
+ private final Context mContext;
+
+ private final class DeserializationToken {
+ int currentIndex = 0;
+ final String source;
+
+ DeserializationToken(String source) {
+ this.source = source;
+ }
+ }
+
+ PhoneAccountRegistrar(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Adds a new phone account entry or updates an existing one.
+ */
+ boolean addAccount(PhoneAccount account) {
+ List<PhoneAccount> allAccounts = getAllAccounts();
+ // Should we implement an artificial limit for # of accounts associated with a single
+ // ComponentName?
+ allAccounts.add(account);
+
+ // Search for duplicates and remove any that are found.
+ for (int i = 0; i < allAccounts.size() - 1; i++) {
+ if (account.equalsComponentAndId(allAccounts.get(i))) {
+ // replace existing entry.
+ allAccounts.remove(i);
+ break;
+ }
+ }
+
+ return writeAllAccounts(allAccounts);
+ }
+
+ /**
+ * Removes an existing phone account entry.
+ */
+ boolean removeAccount(PhoneAccount account) {
+ List<PhoneAccount> allAccounts = getAllAccounts();
+
+ for (int i = 0; i < allAccounts.size(); i++) {
+ if (account.equalsComponentAndId(allAccounts.get(i))) {
+ allAccounts.remove(i);
+ return writeAllAccounts(allAccounts);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns a list of all accounts which the user has enabled.
+ */
+ List<PhoneAccount> getEnabledAccounts() {
+ List<PhoneAccount> allAccounts = getAllAccounts();
+ // TODO: filter list
+ return allAccounts;
+ }
+
+ /**
+ * Returns the list of all accounts registered with the system, whether or not the user
+ * has explicitly enabled them.
+ */
+ List<PhoneAccount> getAllAccounts() {
+ String value = getPreferences().getString(PREFERENCE_PHONE_ACCOUNTS, null);
+ return deserializeAllAccounts(value);
+ }
+
+ /**
+ * Returns the registered version of the account matching the component name and ID of the
+ * specified account.
+ */
+ PhoneAccount getRegisteredAccount(PhoneAccount account) {
+ for (PhoneAccount registeredAccount : getAllAccounts()) {
+ if (registeredAccount.equalsComponentAndId(account)) {
+ return registeredAccount;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Replaces the contents of our list of accounts with this new list.
+ */
+ private boolean writeAllAccounts(List<PhoneAccount> allAccounts) {
+ Editor editor = getPreferences().edit();
+ editor.putString(PREFERENCE_PHONE_ACCOUNTS, serializeAllAccounts(allAccounts));
+ return editor.commit();
+ }
+
+ // Serialization implementation
+ // Serializes all strings into the format "len:string-value"
+ // Example, we will serialize the following PhoneAccount.
+ // PhoneAccount
+ // ComponentName: "abc"
+ // Id: "def"
+ // Handle: "555"
+ // Capabilities: 1
+ //
+ // Each value serializes into (spaces added for readability)
+ // 3:abc 3:def 3:555 1:1
+ //
+ // Two identical accounts would likewise be serialized as a list of strings with a prepended
+ // size of 2.
+ // 1:2 3:abc 3:def 3:555 1:1 3:abc 3:def 3:555 1:1
+ //
+ // The final result with a prepended version ("1:1") would be:
+ // "1:11:23:abc3:def3:5551:13:abc3:def3:5551:1"
+
+ private String serializeAllAccounts(List<PhoneAccount> allAccounts) {
+ StringBuilder buffer = new StringBuilder();
+
+ // Version
+ serializeIntValue(VERSION, buffer);
+
+ // Number of accounts
+ serializeIntValue(allAccounts.size(), buffer);
+
+ // The actual accounts
+ for (int i = 0; i < allAccounts.size(); i++) {
+ PhoneAccount account = allAccounts.get(i);
+ serializeStringValue(account.getComponentName().flattenToShortString(), buffer);
+ serializeStringValue(account.getId(), buffer);
+ serializeStringValue(account.getHandle().toString(), buffer);
+ serializeIntValue(account.getCapabilities(), buffer);
+ }
+
+ return buffer.toString();
+ }
+
+ private List<PhoneAccount> deserializeAllAccounts(String source) {
+ List<PhoneAccount> accounts = new ArrayList<PhoneAccount>();
+
+ if (source != null) {
+ DeserializationToken token = new DeserializationToken(source);
+ int version = deserializeIntValue(token);
+ if (version == 1) {
+ int size = deserializeIntValue(token);
+
+ for (int i = 0; i < size; i++) {
+ String strComponentName = deserializeStringValue(token);
+ String strId = deserializeStringValue(token);
+ String strHandle = deserializeStringValue(token);
+ int capabilities = deserializeIntValue(token);
+
+ accounts.add(new PhoneAccount(
+ ComponentName.unflattenFromString(strComponentName),
+ strId,
+ Uri.parse(strHandle),
+ capabilities));
+ }
+ }
+ }
+
+ return accounts;
+ }
+
+ private void serializeIntValue(int value, StringBuilder buffer) {
+ serializeStringValue(String.valueOf(value), buffer);
+ }
+
+ private void serializeStringValue(String value, StringBuilder buffer) {
+ buffer.append(value.length()).append(":").append(value);
+ }
+
+ private int deserializeIntValue(DeserializationToken token) {
+ return Integer.parseInt(deserializeStringValue(token));
+ }
+
+ private String deserializeStringValue(DeserializationToken token) {
+ int colonIndex = token.source.indexOf(':', token.currentIndex);
+ int valueLength = Integer.parseInt(token.source.substring(token.currentIndex, colonIndex));
+ int endIndex = colonIndex + 1 + valueLength;
+ token.currentIndex = endIndex;
+ return token.source.substring(colonIndex + 1, endIndex);
+ }
+
+ private SharedPreferences getPreferences() {
+ return mContext.getSharedPreferences(TELECOMM_PREFERENCES, Context.MODE_PRIVATE);
+ }
+}