Add store/retrieve of PhoneAccount extras to PhoneAccountRegistry.
- Add write/read to parser.
- Add Unit test case for XML parsing.
- Add test extra data test ConnectionService phone accounts.
Bug: 22806380
Change-Id: Ib01843ee859616eb6674f070a674b792d52b8869
diff --git a/src/com/android/server/telecom/PhoneAccountRegistrar.java b/src/com/android/server/telecom/PhoneAccountRegistrar.java
index a795d6f..5cb6f86 100644
--- a/src/com/android/server/telecom/PhoneAccountRegistrar.java
+++ b/src/com/android/server/telecom/PhoneAccountRegistrar.java
@@ -30,6 +30,7 @@
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Binder;
+import android.os.Bundle;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.UserHandle;
@@ -924,8 +925,13 @@
@VisibleForTesting
public abstract static class XmlSerialization<T> {
- private static final String LENGTH_ATTRIBUTE = "length";
- private static final String VALUE_TAG = "value";
+ private static final String TAG_VALUE = "value";
+ private static final String ATTRIBUTE_LENGTH = "length";
+ private static final String ATTRIBUTE_KEY = "key";
+ private static final String ATTRIBUTE_VALUE_TYPE = "type";
+ private static final String VALUE_TYPE_STRING = "string";
+ private static final String VALUE_TYPE_INTEGER = "integer";
+ private static final String VALUE_TYPE_BOOLEAN = "boolean";
/**
* Write the supplied object to XML
@@ -966,16 +972,51 @@
serializer.startTag(null, tagName);
if (values != null) {
- serializer.attribute(null, LENGTH_ATTRIBUTE, Objects.toString(values.size()));
+ serializer.attribute(null, ATTRIBUTE_LENGTH, Objects.toString(values.size()));
for (String toSerialize : values) {
- serializer.startTag(null, VALUE_TAG);
+ serializer.startTag(null, TAG_VALUE);
if (toSerialize != null ){
serializer.text(toSerialize);
}
- serializer.endTag(null, VALUE_TAG);
+ serializer.endTag(null, TAG_VALUE);
}
} else {
- serializer.attribute(null, LENGTH_ATTRIBUTE, "0");
+ serializer.attribute(null, ATTRIBUTE_LENGTH, "0");
+ }
+ serializer.endTag(null, tagName);
+ }
+
+ protected void writeBundle(String tagName, Bundle values, XmlSerializer serializer)
+ throws IOException {
+
+ serializer.startTag(null, tagName);
+ if (values != null) {
+ for (String key : values.keySet()) {
+ Object value = values.get(key);
+
+ if (value == null) {
+ continue;
+ }
+
+ String valueType;
+ if (value instanceof String) {
+ valueType = VALUE_TYPE_STRING;
+ } else if (value instanceof Integer) {
+ valueType = VALUE_TYPE_INTEGER;
+ } else if (value instanceof Boolean) {
+ valueType = VALUE_TYPE_BOOLEAN;
+ } else {
+ Log.w(this,
+ "PhoneAccounts support only string, integer and boolean extras TY.");
+ continue;
+ }
+
+ serializer.startTag(null, TAG_VALUE);
+ serializer.attribute(null, ATTRIBUTE_KEY, key);
+ serializer.attribute(null, ATTRIBUTE_VALUE_TYPE, valueType);
+ serializer.text(Objects.toString(value));
+ serializer.endTag(null, TAG_VALUE);
+ }
}
serializer.endTag(null, tagName);
}
@@ -1012,7 +1053,7 @@
protected List<String> readStringList(XmlPullParser parser)
throws IOException, XmlPullParserException {
- int length = Integer.parseInt(parser.getAttributeValue(null, LENGTH_ATTRIBUTE));
+ int length = Integer.parseInt(parser.getAttributeValue(null, ATTRIBUTE_LENGTH));
List<String> arrayEntries = new ArrayList<String>(length);
String value = null;
@@ -1022,7 +1063,7 @@
int outerDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
- if (parser.getName().equals(VALUE_TAG)) {
+ if (parser.getName().equals(TAG_VALUE)) {
parser.next();
value = parser.getText();
arrayEntries.add(value);
@@ -1032,6 +1073,55 @@
return arrayEntries;
}
+ /**
+ * Reads a bundle from the XML parser.
+ *
+ * @param parser The XML parser.
+ * @return Bundle containing the parsed values.
+ * @throws IOException Exception related to IO.
+ * @throws XmlPullParserException Exception related to parsing.
+ */
+ protected Bundle readBundle(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+
+ Bundle bundle = null;
+ int outerDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ if (parser.getName().equals(TAG_VALUE)) {
+ String valueType = parser.getAttributeValue(null, ATTRIBUTE_VALUE_TYPE);
+ String key = parser.getAttributeValue(null, ATTRIBUTE_KEY);
+ parser.next();
+ String value = parser.getText();
+
+ if (bundle == null) {
+ bundle = new Bundle();
+ }
+
+ // Do not write null values to the bundle.
+ if (value == null) {
+ continue;
+ }
+
+ if (VALUE_TYPE_STRING.equals(valueType)) {
+ bundle.putString(key, value);
+ } else if (VALUE_TYPE_INTEGER.equals(valueType)) {
+ try {
+ int intValue = Integer.parseInt(value);
+ bundle.putInt(key, intValue);
+ } catch (NumberFormatException nfe) {
+ Log.w(this, "Invalid integer PhoneAccount extra.");
+ }
+ } else if (VALUE_TYPE_BOOLEAN.equals(valueType)) {
+ boolean boolValue = Boolean.parseBoolean(value);
+ bundle.putBoolean(key, boolValue);
+ } else {
+ Log.w(this, "Invalid type " + valueType + " for PhoneAccount bundle.");
+ }
+ }
+ }
+ return bundle;
+ }
+
protected Bitmap readBitmap(XmlPullParser parser) {
byte[] imageByteArray = Base64.decode(parser.getText(), 0);
return BitmapFactory.decodeByteArray(imageByteArray, 0, imageByteArray.length);
@@ -1126,6 +1216,7 @@
private static final String SHORT_DESCRIPTION = "short_description";
private static final String SUPPORTED_URI_SCHEMES = "supported_uri_schemes";
private static final String ICON = "icon";
+ private static final String EXTRAS = "extras";
private static final String ENABLED = "enabled";
@Override
@@ -1149,6 +1240,7 @@
writeTextIfNonNull(LABEL, o.getLabel(), serializer);
writeTextIfNonNull(SHORT_DESCRIPTION, o.getShortDescription(), serializer);
writeStringList(SUPPORTED_URI_SCHEMES, o.getSupportedUriSchemes(), serializer);
+ writeBundle(EXTRAS, o.getExtras(), serializer);
writeTextIfNonNull(ENABLED, o.isEnabled() ? "true" : "false" , serializer);
serializer.endTag(null, CLASS_PHONE_ACCOUNT);
@@ -1173,6 +1265,7 @@
List<String> supportedUriSchemes = null;
Icon icon = null;
boolean enabled = false;
+ Bundle extras = null;
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
if (parser.getName().equals(ACCOUNT_HANDLE)) {
@@ -1218,6 +1311,9 @@
} else if (parser.getName().equals(ENABLED)) {
parser.next();
enabled = "true".equalsIgnoreCase(parser.getText());
+ } else if (parser.getName().equals(EXTRAS)) {
+ parser.next();
+ extras = readBundle(parser);
}
}
@@ -1283,6 +1379,7 @@
.setShortDescription(shortDescription)
.setSupportedUriSchemes(supportedUriSchemes)
.setHighlightColor(highlightColor)
+ .setExtras(extras)
.setIsEnabled(enabled);
if (icon != null) {
diff --git a/testapps/src/com/android/server/telecom/testapps/CallServiceNotifier.java b/testapps/src/com/android/server/telecom/testapps/CallServiceNotifier.java
index c1ced80..28c43c8 100644
--- a/testapps/src/com/android/server/telecom/testapps/CallServiceNotifier.java
+++ b/testapps/src/com/android/server/telecom/testapps/CallServiceNotifier.java
@@ -29,6 +29,7 @@
import android.graphics.Color;
import android.graphics.drawable.Icon;
import android.net.Uri;
+import android.os.Bundle;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
@@ -103,6 +104,14 @@
telecomManager.clearAccounts();
+ Bundle testBundle = new Bundle();
+ testBundle.putInt("EXTRA_INT_1", 1);
+ testBundle.putInt("EXTRA_INT_100", 100);
+ testBundle.putBoolean("EXTRA_BOOL_TRUE", true);
+ testBundle.putBoolean("EXTRA_BOOL_FALSE", false);
+ testBundle.putString("EXTRA_STR1", "Hello");
+ testBundle.putString("EXTRA_STR2", "There");
+
telecomManager.registerPhoneAccount(PhoneAccount.builder(
new PhoneAccountHandle(
new ComponentName(context, TestConnectionService.class),
@@ -115,10 +124,11 @@
PhoneAccount.CAPABILITY_CALL_SUBJECT)
.setIcon(Icon.createWithResource(
context.getResources(), R.drawable.stat_sys_phone_call))
- // TODO: Add icon tint (Color.RED)
.setHighlightColor(Color.RED)
+ // TODO: Add icon tint (Color.RED)
.setShortDescription("a short description for the call provider")
.setSupportedUriSchemes(Arrays.asList("tel"))
+ .setExtras(testBundle)
.build());
telecomManager.registerPhoneAccount(PhoneAccount.builder(
@@ -134,8 +144,8 @@
PhoneAccount.CAPABILITY_CALL_SUBJECT)
.setIcon(Icon.createWithResource(
context.getResources(), R.drawable.stat_sys_phone_call))
- // TODO: Add icon tint (Color.GREEN)
.setHighlightColor(Color.GREEN)
+ // TODO: Add icon tint (Color.GREEN)
.setShortDescription("a short description for the sim subscription")
.build());
diff --git a/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java b/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
index f815fed..b09122d 100644
--- a/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
+++ b/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
@@ -16,6 +16,7 @@
package com.android.server.telecom.tests;
+import android.graphics.Rect;
import android.os.Binder;
import com.android.internal.telecom.IConnectionService;
@@ -32,6 +33,7 @@
import android.graphics.BitmapFactory;
import android.graphics.drawable.Icon;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Parcel;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
@@ -43,6 +45,7 @@
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.util.Arrays;
+import java.util.Set;
public class PhoneAccountRegistrarTest extends TelecomTestCase {
@@ -88,9 +91,18 @@
}
public void testPhoneAccount() throws Exception {
+ Bundle testBundle = new Bundle();
+ testBundle.putInt("EXTRA_INT_1", 1);
+ testBundle.putInt("EXTRA_INT_100", 100);
+ testBundle.putBoolean("EXTRA_BOOL_TRUE", true);
+ testBundle.putBoolean("EXTRA_BOOL_FALSE", false);
+ testBundle.putString("EXTRA_STR1", "Hello");
+ testBundle.putString("EXTRA_STR2", "There");
+
PhoneAccount input = makeQuickAccountBuilder("id0", 0)
.addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
.addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
+ .setExtras(testBundle)
.build();
PhoneAccount result = roundTripXml(this, input, PhoneAccountRegistrar.sPhoneAccountXml,
mContext);
@@ -98,6 +110,38 @@
assertPhoneAccountEquals(input, result);
}
+ /**
+ * Test to ensure non-supported balues
+ * @throws Exception
+ */
+ public void testPhoneAccountExtrasEdge() throws Exception {
+ Bundle testBundle = new Bundle();
+ // Ensure null values for string are not persisted.
+ testBundle.putString("EXTRA_STR2", null);
+ //
+
+ // Ensure unsupported data types are not persisted.
+ testBundle.putShort("EXTRA_SHORT", (short) 2);
+ testBundle.putByte("EXTRA_BYTE", (byte) 1);
+ testBundle.putParcelable("EXTRA_PARC", new Rect(1, 1, 1, 1));
+ // Put in something valid so the bundle exists.
+ testBundle.putString("EXTRA_OK", "OK");
+
+ PhoneAccount input = makeQuickAccountBuilder("id0", 0)
+ .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
+ .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
+ .setExtras(testBundle)
+ .build();
+ PhoneAccount result = roundTripXml(this, input, PhoneAccountRegistrar.sPhoneAccountXml,
+ mContext);
+
+ Bundle extras = result.getExtras();
+ assertFalse(extras.keySet().contains("EXTRA_STR2"));
+ assertFalse(extras.keySet().contains("EXTRA_SHORT"));
+ assertFalse(extras.keySet().contains("EXTRA_BYTE"));
+ assertFalse(extras.keySet().contains("EXTRA_PARC"));
+ }
+
public void testState() throws Exception {
PhoneAccountRegistrar.State input = makeQuickState();
PhoneAccountRegistrar.State result = roundTripXml(this, input,
@@ -330,12 +374,31 @@
assertEquals(a.getLabel(), b.getLabel());
assertEquals(a.getShortDescription(), b.getShortDescription());
assertEquals(a.getSupportedUriSchemes(), b.getSupportedUriSchemes());
+ assertBundlesEqual(a.getExtras(), b.getExtras());
} else {
fail("Phone accounts not equal: " + a + ", " + b);
}
}
}
+ private static void assertBundlesEqual(Bundle a, Bundle b) {
+ if (a == null && b == null) {
+ return;
+ }
+
+ assertNotNull(a);
+ assertNotNull(b);
+ Set<String> keySetA = a.keySet();
+ Set<String> keySetB = b.keySet();
+
+ assertTrue("Bundle keys not the same", keySetA.containsAll(keySetB));
+ assertTrue("Bundle keys not the same", keySetB.containsAll(keySetA));
+
+ for (String keyA : keySetA) {
+ assertEquals("Bundle value not the same", a.get(keyA), b.get(keyA));
+ }
+ }
+
private static void assertStateEquals(
PhoneAccountRegistrar.State a, PhoneAccountRegistrar.State b) {
assertPhoneAccountHandleEquals(a.defaultOutgoing, b.defaultOutgoing);