blob: 56fe975bcc045c4da0f0062c1b33ab22c153debc [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
Ihab Awadb78b2762014-07-25 15:16:23 -070019import com.android.internal.annotations.VisibleForTesting;
20import com.android.internal.util.FastXmlSerializer;
21import com.android.internal.util.XmlUtils;
22
Evan Charlton94d01622014-07-20 12:32:05 -070023import android.telecomm.PhoneAccount;
Evan Charlton89176372014-07-19 18:23:09 -070024import android.telecomm.PhoneAccountHandle;
Ihab Awadb78b2762014-07-25 15:16:23 -070025
26import org.xmlpull.v1.XmlPullParser;
27import org.xmlpull.v1.XmlPullParserException;
28import org.xmlpull.v1.XmlSerializer;
Ihab Awad104f8062014-07-17 11:29:35 -070029
Santos Cordon176ae282014-07-14 02:02:14 -070030import android.content.ComponentName;
31import android.content.Context;
32
Santos Cordon176ae282014-07-14 02:02:14 -070033import android.net.Uri;
Ihab Awad104f8062014-07-17 11:29:35 -070034import android.telecomm.TelecommManager;
Ihab Awadb78b2762014-07-25 15:16:23 -070035import android.util.AtomicFile;
36import android.util.Xml;
Santos Cordon176ae282014-07-14 02:02:14 -070037
Ihab Awadb78b2762014-07-25 15:16:23 -070038import java.io.BufferedInputStream;
39import java.io.BufferedOutputStream;
40import java.io.File;
41import java.io.FileNotFoundException;
42import java.io.FileOutputStream;
43import java.io.IOException;
44import java.io.InputStream;
Santos Cordon176ae282014-07-14 02:02:14 -070045import java.util.ArrayList;
46import java.util.List;
47import java.util.Objects;
Ihab Awadb78b2762014-07-25 15:16:23 -070048import java.util.concurrent.CopyOnWriteArrayList;
Santos Cordon176ae282014-07-14 02:02:14 -070049
50/**
Evan Charlton89176372014-07-19 18:23:09 -070051 * Handles writing and reading PhoneAccountHandle registration entries. This is a simple verbatim
Ihab Awad104f8062014-07-17 11:29:35 -070052 * delegate for all the account handling methods on {@link TelecommManager} as implemented in
53 * {@link TelecommServiceImpl}, with the notable exception that {@link TelecommServiceImpl} is
54 * responsible for security checking to make sure that the caller has proper authority over
Evan Charlton89176372014-07-19 18:23:09 -070055 * the {@code ComponentName}s they are declaring in their {@code PhoneAccountHandle}s.
Santos Cordon176ae282014-07-14 02:02:14 -070056 */
Ihab Awadb78b2762014-07-25 15:16:23 -070057public final class PhoneAccountRegistrar {
Santos Cordon176ae282014-07-14 02:02:14 -070058
Ihab Awadb78b2762014-07-25 15:16:23 -070059 public abstract static class Listener {
60 public void onAccountsChanged(PhoneAccountRegistrar registrar) {}
61 public void onDefaultOutgoingChanged(PhoneAccountRegistrar registrar) {}
62 public void onSimCallManagerChanged(PhoneAccountRegistrar registrar) {}
63 }
64
65 private static final String FILE_NAME = "phone-account-registrar-state.xml";
66
67 private final List<Listener> mListeners = new CopyOnWriteArrayList<>();
68 private final AtomicFile mAtomicFile;
69 private State mState;
Santos Cordon176ae282014-07-14 02:02:14 -070070
Ihab Awad26923222014-07-30 10:54:35 -070071 public PhoneAccountRegistrar(Context context) {
Ihab Awadb78b2762014-07-25 15:16:23 -070072 this(context, FILE_NAME);
73 }
74
75 @VisibleForTesting
76 public PhoneAccountRegistrar(Context context, String fileName) {
77 // TODO: Change file location when Telecomm is part of system
78 mAtomicFile = new AtomicFile(new File(context.getFilesDir(), fileName));
79 mState = new State();
80 read();
Santos Cordon176ae282014-07-14 02:02:14 -070081 }
82
Evan Charlton89176372014-07-19 18:23:09 -070083 public PhoneAccountHandle getDefaultOutgoingPhoneAccount() {
Ihab Awad293edf22014-07-24 17:52:29 -070084 if (mState.defaultOutgoing != null) {
Ihab Awadf2a84912014-07-22 21:09:25 -070085 // Return the registered outgoing default iff it still exists (we keep a sticky
86 // default to survive account deletion and re-addition)
Ihab Awad293edf22014-07-24 17:52:29 -070087 for (int i = 0; i < mState.accounts.size(); i++) {
88 if (mState.accounts.get(i).getAccountHandle().equals(mState.defaultOutgoing)) {
89 return mState.defaultOutgoing;
Ihab Awadf2a84912014-07-22 21:09:25 -070090 }
91 }
Ihab Awad293edf22014-07-24 17:52:29 -070092 // At this point, there was a registered default but it has been deleted; proceed
93 // as though there were no default
94 }
95
96 List<PhoneAccountHandle> enabled = getEnabledPhoneAccounts();
97 switch (enabled.size()) {
98 case 0:
99 // There are no accounts, so there can be no default
100 return null;
101 case 1:
102 // There is only one account, which is by definition the default
103 return enabled.get(0);
104 default:
105 // There are multiple accounts with no selected default
106 return null;
Ihab Awadf2a84912014-07-22 21:09:25 -0700107 }
Ihab Awad104f8062014-07-17 11:29:35 -0700108 }
Santos Cordon176ae282014-07-14 02:02:14 -0700109
Evan Charlton89176372014-07-19 18:23:09 -0700110 public void setDefaultOutgoingPhoneAccount(PhoneAccountHandle accountHandle) {
Evan Charlton89176372014-07-19 18:23:09 -0700111 if (accountHandle == null) {
Ihab Awad104f8062014-07-17 11:29:35 -0700112 // Asking to clear the default outgoing is a valid request
Ihab Awad293edf22014-07-24 17:52:29 -0700113 mState.defaultOutgoing = null;
Ihab Awad104f8062014-07-17 11:29:35 -0700114 } else {
115 boolean found = false;
Ihab Awad293edf22014-07-24 17:52:29 -0700116 for (PhoneAccount m : mState.accounts) {
Evan Charlton94d01622014-07-20 12:32:05 -0700117 if (Objects.equals(accountHandle, m.getAccountHandle())) {
Ihab Awad104f8062014-07-17 11:29:35 -0700118 found = true;
119 break;
120 }
Santos Cordon176ae282014-07-14 02:02:14 -0700121 }
Ihab Awad104f8062014-07-17 11:29:35 -0700122
123 if (!found) {
Ihab Awadb78b2762014-07-25 15:16:23 -0700124 Log.w(this, "Trying to set nonexistent default outgoing %s",
125 accountHandle);
126 return;
127 }
128
129 if (!has(getPhoneAccount(accountHandle), PhoneAccount.CAPABILITY_CALL_PROVIDER)) {
130 Log.w(this, "Trying to set non-call-provider default outgoing %s",
Evan Charlton89176372014-07-19 18:23:09 -0700131 accountHandle);
Ihab Awad104f8062014-07-17 11:29:35 -0700132 return;
133 }
134
Ihab Awad293edf22014-07-24 17:52:29 -0700135 mState.defaultOutgoing = accountHandle;
Santos Cordon176ae282014-07-14 02:02:14 -0700136 }
137
Ihab Awad293edf22014-07-24 17:52:29 -0700138 write();
Ihab Awadb78b2762014-07-25 15:16:23 -0700139 fireDefaultOutgoingChanged();
Santos Cordon176ae282014-07-14 02:02:14 -0700140 }
141
Ihab Awad293edf22014-07-24 17:52:29 -0700142 public void setSimCallManager(PhoneAccountHandle callManager) {
143 if (callManager != null) {
144 PhoneAccount callManagerAccount = getPhoneAccount(callManager);
145 if (callManagerAccount == null) {
146 Log.d(this, "setSimCallManager: Nonexistent call manager: %s", callManager);
147 return;
Ihab Awadb78b2762014-07-25 15:16:23 -0700148 } else if (!has(callManagerAccount, PhoneAccount.CAPABILITY_CONNECTION_MANAGER)) {
Ihab Awad293edf22014-07-24 17:52:29 -0700149 Log.d(this, "setSimCallManager: Not a call manager: %s", callManagerAccount);
150 return;
151 }
152 }
153 mState.simCallManager = callManager;
154 write();
Ihab Awadb78b2762014-07-25 15:16:23 -0700155 fireSimCallManagerChanged();
Ihab Awad293edf22014-07-24 17:52:29 -0700156 }
157
158 public PhoneAccountHandle getSimCallManager() {
Ihab Awadb78b2762014-07-25 15:16:23 -0700159 if (mState.simCallManager != null) {
160 // Return the registered sim call manager iff it still exists (we keep a sticky
161 // setting to survive account deletion and re-addition)
162 for (int i = 0; i < mState.accounts.size(); i++) {
163 if (mState.accounts.get(i).getAccountHandle().equals(mState.simCallManager)) {
164 return mState.simCallManager;
165 }
166 }
167 }
168 return null;
Ihab Awad293edf22014-07-24 17:52:29 -0700169 }
170
171 public List<PhoneAccountHandle> getAllPhoneAccountHandles() {
172 List<PhoneAccountHandle> accountHandles = new ArrayList<>();
173 for (PhoneAccount m : mState.accounts) {
174 accountHandles.add(m.getAccountHandle());
175 }
176 return accountHandles;
177 }
178
179 public List<PhoneAccount> getAllPhoneAccounts() {
180 return new ArrayList<>(mState.accounts);
181 }
182
183 // TODO: Rename systemwide to "getCallProviderPhoneAccounts"?
Evan Charlton89176372014-07-19 18:23:09 -0700184 public List<PhoneAccountHandle> getEnabledPhoneAccounts() {
Ihab Awad293edf22014-07-24 17:52:29 -0700185 return getCallProviderAccountHandles();
Santos Cordon176ae282014-07-14 02:02:14 -0700186 }
187
Ihab Awad293edf22014-07-24 17:52:29 -0700188 public PhoneAccount getPhoneAccount(PhoneAccountHandle handle) {
189 for (PhoneAccount m : mState.accounts) {
190 if (Objects.equals(handle, m.getAccountHandle())) {
Ihab Awad104f8062014-07-17 11:29:35 -0700191 return m;
Santos Cordon176ae282014-07-14 02:02:14 -0700192 }
193 }
194 return null;
195 }
196
Ihab Awad104f8062014-07-17 11:29:35 -0700197 // TODO: Should we implement an artificial limit for # of accounts associated with a single
198 // ComponentName?
Ihab Awad293edf22014-07-24 17:52:29 -0700199 public void registerPhoneAccount(PhoneAccount account) {
200 account = hackFixBabelAccount(account);
201 mState.accounts.add(account);
Ihab Awad104f8062014-07-17 11:29:35 -0700202 // Search for duplicates and remove any that are found.
Ihab Awad293edf22014-07-24 17:52:29 -0700203 for (int i = 0; i < mState.accounts.size() - 1; i++) {
204 if (Objects.equals(
205 account.getAccountHandle(), mState.accounts.get(i).getAccountHandle())) {
Ihab Awad104f8062014-07-17 11:29:35 -0700206 // replace existing entry.
Ihab Awad293edf22014-07-24 17:52:29 -0700207 mState.accounts.remove(i);
Ihab Awad104f8062014-07-17 11:29:35 -0700208 break;
Santos Cordon176ae282014-07-14 02:02:14 -0700209 }
210 }
211
Ihab Awad293edf22014-07-24 17:52:29 -0700212 write();
Ihab Awadb78b2762014-07-25 15:16:23 -0700213 fireAccountsChanged();
Ihab Awad293edf22014-07-24 17:52:29 -0700214 }
215
216 // STOPSHIP: Hack to edit the account registered by Babel so it shows up properly
217 private PhoneAccount hackFixBabelAccount(PhoneAccount account) {
218 String pkg = account.getAccountHandle().getComponentName().getPackageName();
219 return "com.google.android.talk".equals(pkg)
220 ? new PhoneAccount(
221 account.getAccountHandle(),
222 account.getHandle(),
223 account.getSubscriptionNumber(),
Ihab Awadb78b2762014-07-25 15:16:23 -0700224 PhoneAccount.CAPABILITY_CONNECTION_MANAGER,
Ihab Awad293edf22014-07-24 17:52:29 -0700225 account.getIconResId(),
226 account.getLabel(),
Ihab Awadb78b2762014-07-25 15:16:23 -0700227 account.getShortDescription())
Ihab Awad293edf22014-07-24 17:52:29 -0700228 : account;
Santos Cordon176ae282014-07-14 02:02:14 -0700229 }
230
Evan Charlton89176372014-07-19 18:23:09 -0700231 public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) {
Ihab Awad293edf22014-07-24 17:52:29 -0700232 for (int i = 0; i < mState.accounts.size(); i++) {
233 if (Objects.equals(accountHandle, mState.accounts.get(i).getAccountHandle())) {
234 mState.accounts.remove(i);
Ihab Awad104f8062014-07-17 11:29:35 -0700235 break;
236 }
237 }
238
Ihab Awad293edf22014-07-24 17:52:29 -0700239 write();
Ihab Awadb78b2762014-07-25 15:16:23 -0700240 fireAccountsChanged();
Santos Cordon176ae282014-07-14 02:02:14 -0700241 }
242
Ihab Awad104f8062014-07-17 11:29:35 -0700243 public void clearAccounts(String packageName) {
Ihab Awad293edf22014-07-24 17:52:29 -0700244 for (int i = 0; i < mState.accounts.size(); i++) {
Ihab Awad104f8062014-07-17 11:29:35 -0700245 if (Objects.equals(
246 packageName,
Ihab Awad293edf22014-07-24 17:52:29 -0700247 mState.accounts.get(i).getAccountHandle()
248 .getComponentName().getPackageName())) {
249 mState.accounts.remove(i);
Ihab Awad104f8062014-07-17 11:29:35 -0700250 }
251 }
252
Ihab Awad293edf22014-07-24 17:52:29 -0700253 write();
Ihab Awadb78b2762014-07-25 15:16:23 -0700254 fireAccountsChanged();
255 }
256
257 public void addListener(Listener l) {
258 mListeners.add(l);
259 }
260
261 public void removeListener(Listener l) {
262 mListeners.remove(l);
263 }
264
265 private void fireAccountsChanged() {
266 for (Listener l : mListeners) {
267 l.onAccountsChanged(this);
268 }
269 }
270
271 private void fireDefaultOutgoingChanged() {
272 for (Listener l : mListeners) {
273 l.onDefaultOutgoingChanged(this);
274 }
275 }
276
277 private void fireSimCallManagerChanged() {
278 for (Listener l : mListeners) {
279 l.onSimCallManagerChanged(this);
280 }
Santos Cordon176ae282014-07-14 02:02:14 -0700281 }
282
Ihab Awad293edf22014-07-24 17:52:29 -0700283 ////////////////////////////////////////////////////////////////////////////////////////////////
284
285 // TODO: Add a corresponding has(...) method to class PhoneAccount itself and remove this one
286 // Return true iff the given account has all the specified capability flags
287 static boolean has(PhoneAccount account, int capability) {
288 return (account.getCapabilities() & capability) == capability;
289 }
290
291 private List<PhoneAccountHandle> getCallProviderAccountHandles() {
Evan Charlton94d01622014-07-20 12:32:05 -0700292 List<PhoneAccountHandle> accountHandles = new ArrayList<>();
Ihab Awad293edf22014-07-24 17:52:29 -0700293 for (PhoneAccount m : mState.accounts) {
294 if (has(m, PhoneAccount.CAPABILITY_CALL_PROVIDER)) {
Ihab Awadf2a84912014-07-22 21:09:25 -0700295 accountHandles.add(m.getAccountHandle());
296 }
Ihab Awad104f8062014-07-17 11:29:35 -0700297 }
Evan Charlton94d01622014-07-20 12:32:05 -0700298 return accountHandles;
Ihab Awad104f8062014-07-17 11:29:35 -0700299 }
300
Ihab Awad293edf22014-07-24 17:52:29 -0700301 /**
302 * The state of this {@code PhoneAccountRegistrar}.
303 */
Ihab Awadb78b2762014-07-25 15:16:23 -0700304 @VisibleForTesting
305 public static class State {
Ihab Awad293edf22014-07-24 17:52:29 -0700306 /**
307 * The account selected by the user to be employed by default for making outgoing calls.
308 * If the user has not made such a selection, then this is null.
309 */
310 public PhoneAccountHandle defaultOutgoing = null;
311
312 /**
Ihab Awadb78b2762014-07-25 15:16:23 -0700313 * A {@code PhoneAccount} having {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} which
Ihab Awad293edf22014-07-24 17:52:29 -0700314 * manages and optimizes a user's PSTN SIM connections.
315 */
316 public PhoneAccountHandle simCallManager;
317
318 /**
319 * The complete list of {@code PhoneAccount}s known to the Telecomm subsystem.
320 */
321 public final List<PhoneAccount> accounts = new ArrayList<>();
322 }
323
324 ////////////////////////////////////////////////////////////////////////////////////////////////
325 //
326 // State management
327 //
328
329 private void write() {
Ihab Awadb78b2762014-07-25 15:16:23 -0700330 final FileOutputStream os;
Ihab Awad104f8062014-07-17 11:29:35 -0700331 try {
Ihab Awadb78b2762014-07-25 15:16:23 -0700332 os = mAtomicFile.startWrite();
333 boolean success = false;
334 try {
335 XmlSerializer serializer = new FastXmlSerializer();
336 serializer.setOutput(new BufferedOutputStream(os), "utf-8");
337 writeToXml(mState, serializer);
338 serializer.flush();
339 success = true;
340 } finally {
341 if (success) {
342 mAtomicFile.finishWrite(os);
343 } else {
344 mAtomicFile.failWrite(os);
345 }
346 }
347 } catch (IOException e) {
348 Log.e(this, e, "Writing state to XML file");
Ihab Awad104f8062014-07-17 11:29:35 -0700349 }
350 }
351
Ihab Awadb78b2762014-07-25 15:16:23 -0700352 private void read() {
353 final InputStream is;
Ihab Awad104f8062014-07-17 11:29:35 -0700354 try {
Ihab Awadb78b2762014-07-25 15:16:23 -0700355 is = mAtomicFile.openRead();
356 } catch (FileNotFoundException ex) {
357 return;
358 }
359
360 XmlPullParser parser;
361 try {
362 parser = Xml.newPullParser();
363 parser.setInput(new BufferedInputStream(is), null);
364 parser.nextTag();
365 mState = readFromXml(parser);
366 } catch (IOException | XmlPullParserException e) {
367 Log.e(this, e, "Reading state from XML file");
368 mState = new State();
369 } finally {
370 try {
371 is.close();
372 } catch (IOException e) {
373 Log.e(this, e, "Closing InputStream");
374 }
Ihab Awad104f8062014-07-17 11:29:35 -0700375 }
Santos Cordon176ae282014-07-14 02:02:14 -0700376 }
377
Ihab Awadb78b2762014-07-25 15:16:23 -0700378 private static void writeToXml(State state, XmlSerializer serializer)
379 throws IOException {
380 sStateXml.writeToXml(state, serializer);
Santos Cordon176ae282014-07-14 02:02:14 -0700381 }
Ihab Awad104f8062014-07-17 11:29:35 -0700382
Ihab Awadb78b2762014-07-25 15:16:23 -0700383 private static State readFromXml(XmlPullParser parser)
384 throws IOException, XmlPullParserException {
385 State s = sStateXml.readFromXml(parser);
386 return s != null ? s : new State();
Ihab Awad104f8062014-07-17 11:29:35 -0700387 }
388
Ihab Awad293edf22014-07-24 17:52:29 -0700389 ////////////////////////////////////////////////////////////////////////////////////////////////
Ihab Awad104f8062014-07-17 11:29:35 -0700390 //
Ihab Awadb78b2762014-07-25 15:16:23 -0700391 // XML serialization
Ihab Awad104f8062014-07-17 11:29:35 -0700392 //
393
Ihab Awadb78b2762014-07-25 15:16:23 -0700394 @VisibleForTesting
Ihab Awad26923222014-07-30 10:54:35 -0700395 public abstract static class XmlSerialization<T> {
Ihab Awadb78b2762014-07-25 15:16:23 -0700396 /**
397 * Write the supplied object to XML
398 */
Ihab Awad26923222014-07-30 10:54:35 -0700399 public abstract void writeToXml(T o, XmlSerializer serializer)
400 throws IOException;
Ihab Awadb78b2762014-07-25 15:16:23 -0700401
402 /**
403 * Read from the supplied XML into a new object, returning null in case of an
404 * unrecoverable schema mismatch or other data error. 'parser' must be already
405 * positioned at the first tag that is expected to have been emitted by this
406 * object's writeToXml(). This object tries to fail early without modifying
407 * 'parser' if it does not recognize the data it sees.
408 */
Ihab Awad26923222014-07-30 10:54:35 -0700409 public abstract T readFromXml(XmlPullParser parser)
410 throws IOException, XmlPullParserException;
411
412 protected void writeTextSafely(String tagName, Object value, XmlSerializer serializer)
413 throws IOException {
414 if (value != null) {
415 serializer.startTag(null, tagName);
416 serializer.text(Objects.toString(value));
417 serializer.endTag(null, tagName);
418 }
419 }
Ihab Awad104f8062014-07-17 11:29:35 -0700420 }
421
Ihab Awadb78b2762014-07-25 15:16:23 -0700422 @VisibleForTesting
423 public static final XmlSerialization<State> sStateXml =
424 new XmlSerialization<State>() {
425 private static final String CLASS_STATE = "phone_account_registrar_state";
Ihab Awad104f8062014-07-17 11:29:35 -0700426 private static final String DEFAULT_OUTGOING = "default_outgoing";
Ihab Awad293edf22014-07-24 17:52:29 -0700427 private static final String SIM_CALL_MANAGER = "sim_call_manager";
Ihab Awad104f8062014-07-17 11:29:35 -0700428 private static final String ACCOUNTS = "accounts";
429
430 @Override
Ihab Awadb78b2762014-07-25 15:16:23 -0700431 public void writeToXml(State o, XmlSerializer serializer)
432 throws IOException {
Ihab Awad26923222014-07-30 10:54:35 -0700433 if (o != null) {
434 serializer.startTag(null, CLASS_STATE);
Ihab Awadb78b2762014-07-25 15:16:23 -0700435
Ihab Awad26923222014-07-30 10:54:35 -0700436 if (o.defaultOutgoing != null) {
437 serializer.startTag(null, DEFAULT_OUTGOING);
438 sPhoneAccountHandleXml.writeToXml(o.defaultOutgoing, serializer);
439 serializer.endTag(null, DEFAULT_OUTGOING);
440 }
441
442 if (o.simCallManager != null) {
443 serializer.startTag(null, SIM_CALL_MANAGER);
444 sPhoneAccountHandleXml.writeToXml(o.simCallManager, serializer);
445 serializer.endTag(null, SIM_CALL_MANAGER);
446 }
447
448 serializer.startTag(null, ACCOUNTS);
449 for (PhoneAccount m : o.accounts) {
450 sPhoneAccountXml.writeToXml(m, serializer);
451 }
452 serializer.endTag(null, ACCOUNTS);
453
454 serializer.endTag(null, CLASS_STATE);
Ihab Awad293edf22014-07-24 17:52:29 -0700455 }
Ihab Awad104f8062014-07-17 11:29:35 -0700456 }
457
458 @Override
Ihab Awadb78b2762014-07-25 15:16:23 -0700459 public State readFromXml(XmlPullParser parser)
460 throws IOException, XmlPullParserException {
461 if (parser.getName().equals(CLASS_STATE)) {
462 State s = new State();
463 int outerDepth = parser.getDepth();
464 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
465 if (parser.getName().equals(DEFAULT_OUTGOING)) {
466 parser.nextTag();
467 s.defaultOutgoing = sPhoneAccountHandleXml.readFromXml(parser);
468 } else if (parser.getName().equals(SIM_CALL_MANAGER)) {
469 parser.nextTag();
470 s.simCallManager = sPhoneAccountHandleXml.readFromXml(parser);
471 } else if (parser.getName().equals(ACCOUNTS)) {
472 int accountsDepth = parser.getDepth();
473 while (XmlUtils.nextElementWithin(parser, accountsDepth)) {
474 PhoneAccount account = sPhoneAccountXml.readFromXml(parser);
475 if (account != null) {
476 s.accounts.add(account);
477 }
478 }
Ihab Awad104f8062014-07-17 11:29:35 -0700479 }
480 }
Ihab Awadb78b2762014-07-25 15:16:23 -0700481 return s;
Ihab Awad104f8062014-07-17 11:29:35 -0700482 }
Ihab Awadb78b2762014-07-25 15:16:23 -0700483 return null;
Ihab Awad104f8062014-07-17 11:29:35 -0700484 }
485 };
486
Ihab Awadb78b2762014-07-25 15:16:23 -0700487 @VisibleForTesting
488 public static final XmlSerialization<PhoneAccount> sPhoneAccountXml =
489 new XmlSerialization<PhoneAccount>() {
490 private static final String CLASS_PHONE_ACCOUNT = "phone_account";
491 private static final String ACCOUNT_HANDLE = "account_handle";
Ihab Awad104f8062014-07-17 11:29:35 -0700492 private static final String HANDLE = "handle";
Evan Charlton484f8d62014-07-18 14:06:58 -0700493 private static final String SUBSCRIPTION_NUMBER = "subscription_number";
Ihab Awad104f8062014-07-17 11:29:35 -0700494 private static final String CAPABILITIES = "capabilities";
495 private static final String ICON_RES_ID = "icon_res_id";
496 private static final String LABEL = "label";
497 private static final String SHORT_DESCRIPTION = "short_description";
Ihab Awad104f8062014-07-17 11:29:35 -0700498
499 @Override
Ihab Awadb78b2762014-07-25 15:16:23 -0700500 public void writeToXml(PhoneAccount o, XmlSerializer serializer)
501 throws IOException {
Ihab Awad26923222014-07-30 10:54:35 -0700502 if (o != null) {
503 serializer.startTag(null, CLASS_PHONE_ACCOUNT);
Ihab Awadb78b2762014-07-25 15:16:23 -0700504
Ihab Awad26923222014-07-30 10:54:35 -0700505 if (o.getAccountHandle() != null) {
506 serializer.startTag(null, ACCOUNT_HANDLE);
507 sPhoneAccountHandleXml.writeToXml(o.getAccountHandle(), serializer);
508 serializer.endTag(null, ACCOUNT_HANDLE);
509 }
Ihab Awadb78b2762014-07-25 15:16:23 -0700510
Ihab Awad26923222014-07-30 10:54:35 -0700511 writeTextSafely(HANDLE, o.getHandle(), serializer);
512 writeTextSafely(SUBSCRIPTION_NUMBER, o.getSubscriptionNumber(), serializer);
513 writeTextSafely(CAPABILITIES, Integer.toString(o.getCapabilities()), serializer);
514 writeTextSafely(ICON_RES_ID, Integer.toString(o.getIconResId()), serializer);
515 writeTextSafely(LABEL, o.getLabel(), serializer);
516 writeTextSafely(SHORT_DESCRIPTION, o.getShortDescription(), serializer);
Ihab Awadb78b2762014-07-25 15:16:23 -0700517
Ihab Awad26923222014-07-30 10:54:35 -0700518 serializer.endTag(null, CLASS_PHONE_ACCOUNT);
519 }
Ihab Awad104f8062014-07-17 11:29:35 -0700520 }
521
522 @Override
Ihab Awadb78b2762014-07-25 15:16:23 -0700523 public PhoneAccount readFromXml(XmlPullParser parser)
524 throws IOException, XmlPullParserException {
525 if (parser.getName().equals(CLASS_PHONE_ACCOUNT)) {
526 int outerDepth = parser.getDepth();
527 PhoneAccountHandle accountHandle = null;
528 Uri handle = null;
529 String subscriptionNumber = null;
530 int capabilities = 0;
531 int iconResId = 0;
532 String label = null;
533 String shortDescription = null;
534
535 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
536 if (parser.getName().equals(ACCOUNT_HANDLE)) {
537 parser.nextTag();
538 accountHandle = sPhoneAccountHandleXml.readFromXml(parser);
539 } else if (parser.getName().equals(HANDLE)) {
540 parser.next();
541 handle = Uri.parse(parser.getText());
542 } else if (parser.getName().equals(SUBSCRIPTION_NUMBER)) {
543 parser.next();
544 subscriptionNumber = parser.getText();
545 } else if (parser.getName().equals(CAPABILITIES)) {
546 parser.next();
547 capabilities = Integer.parseInt(parser.getText());
548 } else if (parser.getName().equals(ICON_RES_ID)) {
549 parser.next();
550 iconResId = Integer.parseInt(parser.getText());
551 } else if (parser.getName().equals(LABEL)) {
552 parser.next();
553 label = parser.getText();
554 } else if (parser.getName().equals(SHORT_DESCRIPTION)) {
555 parser.next();
556 shortDescription = parser.getText();
557 }
558 }
Ihab Awad26923222014-07-30 10:54:35 -0700559 return new PhoneAccount(
560 accountHandle,
561 handle,
562 subscriptionNumber,
563 capabilities,
564 iconResId,
565 label,
566 shortDescription);
Ihab Awadb78b2762014-07-25 15:16:23 -0700567 }
568 return null;
Ihab Awad104f8062014-07-17 11:29:35 -0700569 }
570 };
571
Ihab Awadb78b2762014-07-25 15:16:23 -0700572 @VisibleForTesting
573 public static final XmlSerialization<PhoneAccountHandle> sPhoneAccountHandleXml =
574 new XmlSerialization<PhoneAccountHandle>() {
575 private static final String CLASS_PHONE_ACCOUNT_HANDLE = "phone_account_handle";
Ihab Awad104f8062014-07-17 11:29:35 -0700576 private static final String COMPONENT_NAME = "component_name";
577 private static final String ID = "id";
578
579 @Override
Ihab Awadb78b2762014-07-25 15:16:23 -0700580 public void writeToXml(PhoneAccountHandle o, XmlSerializer serializer)
581 throws IOException {
Ihab Awad26923222014-07-30 10:54:35 -0700582 if (o != null) {
583 serializer.startTag(null, CLASS_PHONE_ACCOUNT_HANDLE);
Ihab Awadb78b2762014-07-25 15:16:23 -0700584
Ihab Awad26923222014-07-30 10:54:35 -0700585 if (o.getComponentName() != null) {
586 writeTextSafely(
587 COMPONENT_NAME, o.getComponentName().flattenToString(), serializer);
588 }
Ihab Awadb78b2762014-07-25 15:16:23 -0700589
Ihab Awad26923222014-07-30 10:54:35 -0700590 writeTextSafely(ID, o.getId(), serializer);
Ihab Awadb78b2762014-07-25 15:16:23 -0700591
Ihab Awad26923222014-07-30 10:54:35 -0700592 serializer.endTag(null, CLASS_PHONE_ACCOUNT_HANDLE);
593 }
Ihab Awad104f8062014-07-17 11:29:35 -0700594 }
595
596 @Override
Ihab Awadb78b2762014-07-25 15:16:23 -0700597 public PhoneAccountHandle readFromXml(XmlPullParser parser)
598 throws IOException, XmlPullParserException {
599 if (parser.getName().equals(CLASS_PHONE_ACCOUNT_HANDLE)) {
600 String componentNameString = null;
601 String idString = null;
602 int outerDepth = parser.getDepth();
603 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
604 if (parser.getName().equals(COMPONENT_NAME)) {
605 parser.next();
606 componentNameString = parser.getText();
607 } else if (parser.getName().equals(ID)) {
608 parser.next();
609 idString = parser.getText();
610 }
611 }
Ihab Awad26923222014-07-30 10:54:35 -0700612 if (componentNameString != null) {
Ihab Awadb78b2762014-07-25 15:16:23 -0700613 return new PhoneAccountHandle(
614 ComponentName.unflattenFromString(componentNameString),
615 idString);
616 }
617 }
618 return null;
Ihab Awad104f8062014-07-17 11:29:35 -0700619 }
620 };
Santos Cordon176ae282014-07-14 02:02:14 -0700621}