blob: 09ff54794e9c4163d83ee77b37a769c31b53bca0 [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
Evan Charlton94d01622014-07-20 12:32:05 -070019import android.telecomm.PhoneAccount;
Evan Charlton89176372014-07-19 18:23:09 -070020import android.telecomm.PhoneAccountHandle;
Santos Cordon176ae282014-07-14 02:02:14 -070021import android.content.ComponentName;
22import android.content.Context;
Santos Cordon176ae282014-07-14 02:02:14 -070023import android.net.Uri;
Ihab Awad104f8062014-07-17 11:29:35 -070024import android.telecomm.TelecommManager;
Ihab Awadb78b2762014-07-25 15:16:23 -070025import android.util.AtomicFile;
26import android.util.Xml;
Santos Cordon176ae282014-07-14 02:02:14 -070027
Sailesh Nepal0e1dc5a2014-07-30 11:08:54 -070028import com.android.internal.annotations.VisibleForTesting;
29import com.android.internal.util.FastXmlSerializer;
30import com.android.internal.util.XmlUtils;
31
Ihab Awadb78b2762014-07-25 15:16:23 -070032import java.io.BufferedInputStream;
33import java.io.BufferedOutputStream;
34import java.io.File;
35import java.io.FileNotFoundException;
36import java.io.FileOutputStream;
37import java.io.IOException;
38import java.io.InputStream;
Santos Cordon176ae282014-07-14 02:02:14 -070039import java.util.ArrayList;
40import java.util.List;
41import java.util.Objects;
Ihab Awadb78b2762014-07-25 15:16:23 -070042import java.util.concurrent.CopyOnWriteArrayList;
Santos Cordon176ae282014-07-14 02:02:14 -070043
Sailesh Nepal0e1dc5a2014-07-30 11:08:54 -070044import org.xmlpull.v1.XmlPullParser;
45import org.xmlpull.v1.XmlPullParserException;
46import org.xmlpull.v1.XmlSerializer;
47
Santos Cordon176ae282014-07-14 02:02:14 -070048/**
Evan Charlton89176372014-07-19 18:23:09 -070049 * Handles writing and reading PhoneAccountHandle registration entries. This is a simple verbatim
Ihab Awad104f8062014-07-17 11:29:35 -070050 * delegate for all the account handling methods on {@link TelecommManager} as implemented in
51 * {@link TelecommServiceImpl}, with the notable exception that {@link TelecommServiceImpl} is
52 * responsible for security checking to make sure that the caller has proper authority over
Evan Charlton89176372014-07-19 18:23:09 -070053 * the {@code ComponentName}s they are declaring in their {@code PhoneAccountHandle}s.
Santos Cordon176ae282014-07-14 02:02:14 -070054 */
Ihab Awadb78b2762014-07-25 15:16:23 -070055public final class PhoneAccountRegistrar {
Santos Cordon176ae282014-07-14 02:02:14 -070056
Ihab Awadb78b2762014-07-25 15:16:23 -070057 public abstract static class Listener {
58 public void onAccountsChanged(PhoneAccountRegistrar registrar) {}
59 public void onDefaultOutgoingChanged(PhoneAccountRegistrar registrar) {}
60 public void onSimCallManagerChanged(PhoneAccountRegistrar registrar) {}
61 }
62
63 private static final String FILE_NAME = "phone-account-registrar-state.xml";
64
65 private final List<Listener> mListeners = new CopyOnWriteArrayList<>();
66 private final AtomicFile mAtomicFile;
67 private State mState;
Santos Cordon176ae282014-07-14 02:02:14 -070068
Ihab Awad26923222014-07-30 10:54:35 -070069 public PhoneAccountRegistrar(Context context) {
Ihab Awadb78b2762014-07-25 15:16:23 -070070 this(context, FILE_NAME);
71 }
72
73 @VisibleForTesting
74 public PhoneAccountRegistrar(Context context, String fileName) {
75 // TODO: Change file location when Telecomm is part of system
76 mAtomicFile = new AtomicFile(new File(context.getFilesDir(), fileName));
77 mState = new State();
78 read();
Santos Cordon176ae282014-07-14 02:02:14 -070079 }
80
Evan Charlton89176372014-07-19 18:23:09 -070081 public PhoneAccountHandle getDefaultOutgoingPhoneAccount() {
Ihab Awad293edf22014-07-24 17:52:29 -070082 if (mState.defaultOutgoing != null) {
Ihab Awadf2a84912014-07-22 21:09:25 -070083 // Return the registered outgoing default iff it still exists (we keep a sticky
84 // default to survive account deletion and re-addition)
Ihab Awad293edf22014-07-24 17:52:29 -070085 for (int i = 0; i < mState.accounts.size(); i++) {
86 if (mState.accounts.get(i).getAccountHandle().equals(mState.defaultOutgoing)) {
87 return mState.defaultOutgoing;
Ihab Awadf2a84912014-07-22 21:09:25 -070088 }
89 }
Ihab Awad293edf22014-07-24 17:52:29 -070090 // At this point, there was a registered default but it has been deleted; proceed
91 // as though there were no default
92 }
93
94 List<PhoneAccountHandle> enabled = getEnabledPhoneAccounts();
95 switch (enabled.size()) {
96 case 0:
97 // There are no accounts, so there can be no default
98 return null;
99 case 1:
100 // There is only one account, which is by definition the default
101 return enabled.get(0);
102 default:
103 // There are multiple accounts with no selected default
104 return null;
Ihab Awadf2a84912014-07-22 21:09:25 -0700105 }
Ihab Awad104f8062014-07-17 11:29:35 -0700106 }
Santos Cordon176ae282014-07-14 02:02:14 -0700107
Evan Charlton89176372014-07-19 18:23:09 -0700108 public void setDefaultOutgoingPhoneAccount(PhoneAccountHandle accountHandle) {
Evan Charlton89176372014-07-19 18:23:09 -0700109 if (accountHandle == null) {
Ihab Awad104f8062014-07-17 11:29:35 -0700110 // Asking to clear the default outgoing is a valid request
Ihab Awad293edf22014-07-24 17:52:29 -0700111 mState.defaultOutgoing = null;
Ihab Awad104f8062014-07-17 11:29:35 -0700112 } else {
113 boolean found = false;
Ihab Awad293edf22014-07-24 17:52:29 -0700114 for (PhoneAccount m : mState.accounts) {
Evan Charlton94d01622014-07-20 12:32:05 -0700115 if (Objects.equals(accountHandle, m.getAccountHandle())) {
Ihab Awad104f8062014-07-17 11:29:35 -0700116 found = true;
117 break;
118 }
Santos Cordon176ae282014-07-14 02:02:14 -0700119 }
Ihab Awad104f8062014-07-17 11:29:35 -0700120
121 if (!found) {
Ihab Awadb78b2762014-07-25 15:16:23 -0700122 Log.w(this, "Trying to set nonexistent default outgoing %s",
123 accountHandle);
124 return;
125 }
126
127 if (!has(getPhoneAccount(accountHandle), PhoneAccount.CAPABILITY_CALL_PROVIDER)) {
128 Log.w(this, "Trying to set non-call-provider default outgoing %s",
Evan Charlton89176372014-07-19 18:23:09 -0700129 accountHandle);
Ihab Awad104f8062014-07-17 11:29:35 -0700130 return;
131 }
132
Ihab Awad293edf22014-07-24 17:52:29 -0700133 mState.defaultOutgoing = accountHandle;
Santos Cordon176ae282014-07-14 02:02:14 -0700134 }
135
Ihab Awad293edf22014-07-24 17:52:29 -0700136 write();
Ihab Awadb78b2762014-07-25 15:16:23 -0700137 fireDefaultOutgoingChanged();
Santos Cordon176ae282014-07-14 02:02:14 -0700138 }
139
Ihab Awad293edf22014-07-24 17:52:29 -0700140 public void setSimCallManager(PhoneAccountHandle callManager) {
141 if (callManager != null) {
142 PhoneAccount callManagerAccount = getPhoneAccount(callManager);
143 if (callManagerAccount == null) {
144 Log.d(this, "setSimCallManager: Nonexistent call manager: %s", callManager);
145 return;
Ihab Awadb78b2762014-07-25 15:16:23 -0700146 } else if (!has(callManagerAccount, PhoneAccount.CAPABILITY_CONNECTION_MANAGER)) {
Ihab Awad293edf22014-07-24 17:52:29 -0700147 Log.d(this, "setSimCallManager: Not a call manager: %s", callManagerAccount);
148 return;
149 }
150 }
151 mState.simCallManager = callManager;
152 write();
Ihab Awadb78b2762014-07-25 15:16:23 -0700153 fireSimCallManagerChanged();
Ihab Awad293edf22014-07-24 17:52:29 -0700154 }
155
156 public PhoneAccountHandle getSimCallManager() {
Ihab Awadb78b2762014-07-25 15:16:23 -0700157 if (mState.simCallManager != null) {
158 // Return the registered sim call manager iff it still exists (we keep a sticky
159 // setting to survive account deletion and re-addition)
160 for (int i = 0; i < mState.accounts.size(); i++) {
161 if (mState.accounts.get(i).getAccountHandle().equals(mState.simCallManager)) {
162 return mState.simCallManager;
163 }
164 }
165 }
166 return null;
Ihab Awad293edf22014-07-24 17:52:29 -0700167 }
168
169 public List<PhoneAccountHandle> getAllPhoneAccountHandles() {
170 List<PhoneAccountHandle> accountHandles = new ArrayList<>();
171 for (PhoneAccount m : mState.accounts) {
172 accountHandles.add(m.getAccountHandle());
173 }
174 return accountHandles;
175 }
176
177 public List<PhoneAccount> getAllPhoneAccounts() {
178 return new ArrayList<>(mState.accounts);
179 }
180
181 // TODO: Rename systemwide to "getCallProviderPhoneAccounts"?
Evan Charlton89176372014-07-19 18:23:09 -0700182 public List<PhoneAccountHandle> getEnabledPhoneAccounts() {
Ihab Awad293edf22014-07-24 17:52:29 -0700183 return getCallProviderAccountHandles();
Santos Cordon176ae282014-07-14 02:02:14 -0700184 }
185
Ihab Awad293edf22014-07-24 17:52:29 -0700186 public PhoneAccount getPhoneAccount(PhoneAccountHandle handle) {
187 for (PhoneAccount m : mState.accounts) {
188 if (Objects.equals(handle, m.getAccountHandle())) {
Ihab Awad104f8062014-07-17 11:29:35 -0700189 return m;
Santos Cordon176ae282014-07-14 02:02:14 -0700190 }
191 }
192 return null;
193 }
194
Ihab Awad104f8062014-07-17 11:29:35 -0700195 // TODO: Should we implement an artificial limit for # of accounts associated with a single
196 // ComponentName?
Ihab Awad293edf22014-07-24 17:52:29 -0700197 public void registerPhoneAccount(PhoneAccount account) {
Ihab Awad293edf22014-07-24 17:52:29 -0700198 mState.accounts.add(account);
Ihab Awad104f8062014-07-17 11:29:35 -0700199 // Search for duplicates and remove any that are found.
Ihab Awad293edf22014-07-24 17:52:29 -0700200 for (int i = 0; i < mState.accounts.size() - 1; i++) {
201 if (Objects.equals(
202 account.getAccountHandle(), mState.accounts.get(i).getAccountHandle())) {
Ihab Awad104f8062014-07-17 11:29:35 -0700203 // replace existing entry.
Ihab Awad293edf22014-07-24 17:52:29 -0700204 mState.accounts.remove(i);
Ihab Awad104f8062014-07-17 11:29:35 -0700205 break;
Santos Cordon176ae282014-07-14 02:02:14 -0700206 }
207 }
208
Ihab Awad293edf22014-07-24 17:52:29 -0700209 write();
Ihab Awadb78b2762014-07-25 15:16:23 -0700210 fireAccountsChanged();
Ihab Awad293edf22014-07-24 17:52:29 -0700211 }
212
Evan Charlton89176372014-07-19 18:23:09 -0700213 public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) {
Ihab Awad293edf22014-07-24 17:52:29 -0700214 for (int i = 0; i < mState.accounts.size(); i++) {
215 if (Objects.equals(accountHandle, mState.accounts.get(i).getAccountHandle())) {
216 mState.accounts.remove(i);
Ihab Awad104f8062014-07-17 11:29:35 -0700217 break;
218 }
219 }
220
Ihab Awad293edf22014-07-24 17:52:29 -0700221 write();
Ihab Awadb78b2762014-07-25 15:16:23 -0700222 fireAccountsChanged();
Santos Cordon176ae282014-07-14 02:02:14 -0700223 }
224
Ihab Awad104f8062014-07-17 11:29:35 -0700225 public void clearAccounts(String packageName) {
Ihab Awad293edf22014-07-24 17:52:29 -0700226 for (int i = 0; i < mState.accounts.size(); i++) {
Ihab Awad104f8062014-07-17 11:29:35 -0700227 if (Objects.equals(
228 packageName,
Ihab Awad293edf22014-07-24 17:52:29 -0700229 mState.accounts.get(i).getAccountHandle()
230 .getComponentName().getPackageName())) {
231 mState.accounts.remove(i);
Ihab Awad104f8062014-07-17 11:29:35 -0700232 }
233 }
234
Ihab Awad293edf22014-07-24 17:52:29 -0700235 write();
Ihab Awadb78b2762014-07-25 15:16:23 -0700236 fireAccountsChanged();
237 }
238
239 public void addListener(Listener l) {
240 mListeners.add(l);
241 }
242
243 public void removeListener(Listener l) {
244 mListeners.remove(l);
245 }
246
247 private void fireAccountsChanged() {
248 for (Listener l : mListeners) {
249 l.onAccountsChanged(this);
250 }
251 }
252
253 private void fireDefaultOutgoingChanged() {
254 for (Listener l : mListeners) {
255 l.onDefaultOutgoingChanged(this);
256 }
257 }
258
259 private void fireSimCallManagerChanged() {
260 for (Listener l : mListeners) {
261 l.onSimCallManagerChanged(this);
262 }
Santos Cordon176ae282014-07-14 02:02:14 -0700263 }
264
Ihab Awad293edf22014-07-24 17:52:29 -0700265 ////////////////////////////////////////////////////////////////////////////////////////////////
266
267 // TODO: Add a corresponding has(...) method to class PhoneAccount itself and remove this one
268 // Return true iff the given account has all the specified capability flags
269 static boolean has(PhoneAccount account, int capability) {
270 return (account.getCapabilities() & capability) == capability;
271 }
272
273 private List<PhoneAccountHandle> getCallProviderAccountHandles() {
Evan Charlton94d01622014-07-20 12:32:05 -0700274 List<PhoneAccountHandle> accountHandles = new ArrayList<>();
Ihab Awad293edf22014-07-24 17:52:29 -0700275 for (PhoneAccount m : mState.accounts) {
276 if (has(m, PhoneAccount.CAPABILITY_CALL_PROVIDER)) {
Ihab Awadf2a84912014-07-22 21:09:25 -0700277 accountHandles.add(m.getAccountHandle());
278 }
Ihab Awad104f8062014-07-17 11:29:35 -0700279 }
Evan Charlton94d01622014-07-20 12:32:05 -0700280 return accountHandles;
Ihab Awad104f8062014-07-17 11:29:35 -0700281 }
282
Ihab Awad293edf22014-07-24 17:52:29 -0700283 /**
284 * The state of this {@code PhoneAccountRegistrar}.
285 */
Ihab Awadb78b2762014-07-25 15:16:23 -0700286 @VisibleForTesting
287 public static class State {
Ihab Awad293edf22014-07-24 17:52:29 -0700288 /**
289 * The account selected by the user to be employed by default for making outgoing calls.
290 * If the user has not made such a selection, then this is null.
291 */
292 public PhoneAccountHandle defaultOutgoing = null;
293
294 /**
Ihab Awadb78b2762014-07-25 15:16:23 -0700295 * A {@code PhoneAccount} having {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} which
Ihab Awad293edf22014-07-24 17:52:29 -0700296 * manages and optimizes a user's PSTN SIM connections.
297 */
298 public PhoneAccountHandle simCallManager;
299
300 /**
301 * The complete list of {@code PhoneAccount}s known to the Telecomm subsystem.
302 */
303 public final List<PhoneAccount> accounts = new ArrayList<>();
304 }
305
306 ////////////////////////////////////////////////////////////////////////////////////////////////
307 //
308 // State management
309 //
310
311 private void write() {
Ihab Awadb78b2762014-07-25 15:16:23 -0700312 final FileOutputStream os;
Ihab Awad104f8062014-07-17 11:29:35 -0700313 try {
Ihab Awadb78b2762014-07-25 15:16:23 -0700314 os = mAtomicFile.startWrite();
315 boolean success = false;
316 try {
317 XmlSerializer serializer = new FastXmlSerializer();
318 serializer.setOutput(new BufferedOutputStream(os), "utf-8");
319 writeToXml(mState, serializer);
320 serializer.flush();
321 success = true;
322 } finally {
323 if (success) {
324 mAtomicFile.finishWrite(os);
325 } else {
326 mAtomicFile.failWrite(os);
327 }
328 }
329 } catch (IOException e) {
330 Log.e(this, e, "Writing state to XML file");
Ihab Awad104f8062014-07-17 11:29:35 -0700331 }
332 }
333
Ihab Awadb78b2762014-07-25 15:16:23 -0700334 private void read() {
335 final InputStream is;
Ihab Awad104f8062014-07-17 11:29:35 -0700336 try {
Ihab Awadb78b2762014-07-25 15:16:23 -0700337 is = mAtomicFile.openRead();
338 } catch (FileNotFoundException ex) {
339 return;
340 }
341
342 XmlPullParser parser;
343 try {
344 parser = Xml.newPullParser();
345 parser.setInput(new BufferedInputStream(is), null);
346 parser.nextTag();
347 mState = readFromXml(parser);
348 } catch (IOException | XmlPullParserException e) {
349 Log.e(this, e, "Reading state from XML file");
350 mState = new State();
351 } finally {
352 try {
353 is.close();
354 } catch (IOException e) {
355 Log.e(this, e, "Closing InputStream");
356 }
Ihab Awad104f8062014-07-17 11:29:35 -0700357 }
Santos Cordon176ae282014-07-14 02:02:14 -0700358 }
359
Ihab Awadb78b2762014-07-25 15:16:23 -0700360 private static void writeToXml(State state, XmlSerializer serializer)
361 throws IOException {
362 sStateXml.writeToXml(state, serializer);
Santos Cordon176ae282014-07-14 02:02:14 -0700363 }
Ihab Awad104f8062014-07-17 11:29:35 -0700364
Ihab Awadb78b2762014-07-25 15:16:23 -0700365 private static State readFromXml(XmlPullParser parser)
366 throws IOException, XmlPullParserException {
367 State s = sStateXml.readFromXml(parser);
368 return s != null ? s : new State();
Ihab Awad104f8062014-07-17 11:29:35 -0700369 }
370
Ihab Awad293edf22014-07-24 17:52:29 -0700371 ////////////////////////////////////////////////////////////////////////////////////////////////
Ihab Awad104f8062014-07-17 11:29:35 -0700372 //
Ihab Awadb78b2762014-07-25 15:16:23 -0700373 // XML serialization
Ihab Awad104f8062014-07-17 11:29:35 -0700374 //
375
Ihab Awadb78b2762014-07-25 15:16:23 -0700376 @VisibleForTesting
Ihab Awad26923222014-07-30 10:54:35 -0700377 public abstract static class XmlSerialization<T> {
Ihab Awadb78b2762014-07-25 15:16:23 -0700378 /**
379 * Write the supplied object to XML
380 */
Ihab Awad26923222014-07-30 10:54:35 -0700381 public abstract void writeToXml(T o, XmlSerializer serializer)
382 throws IOException;
Ihab Awadb78b2762014-07-25 15:16:23 -0700383
384 /**
385 * Read from the supplied XML into a new object, returning null in case of an
386 * unrecoverable schema mismatch or other data error. 'parser' must be already
387 * positioned at the first tag that is expected to have been emitted by this
388 * object's writeToXml(). This object tries to fail early without modifying
389 * 'parser' if it does not recognize the data it sees.
390 */
Ihab Awad26923222014-07-30 10:54:35 -0700391 public abstract T readFromXml(XmlPullParser parser)
392 throws IOException, XmlPullParserException;
393
394 protected void writeTextSafely(String tagName, Object value, XmlSerializer serializer)
395 throws IOException {
396 if (value != null) {
397 serializer.startTag(null, tagName);
398 serializer.text(Objects.toString(value));
399 serializer.endTag(null, tagName);
400 }
401 }
Ihab Awad104f8062014-07-17 11:29:35 -0700402 }
403
Ihab Awadb78b2762014-07-25 15:16:23 -0700404 @VisibleForTesting
405 public static final XmlSerialization<State> sStateXml =
406 new XmlSerialization<State>() {
407 private static final String CLASS_STATE = "phone_account_registrar_state";
Ihab Awad104f8062014-07-17 11:29:35 -0700408 private static final String DEFAULT_OUTGOING = "default_outgoing";
Ihab Awad293edf22014-07-24 17:52:29 -0700409 private static final String SIM_CALL_MANAGER = "sim_call_manager";
Ihab Awad104f8062014-07-17 11:29:35 -0700410 private static final String ACCOUNTS = "accounts";
411
412 @Override
Ihab Awadb78b2762014-07-25 15:16:23 -0700413 public void writeToXml(State o, XmlSerializer serializer)
414 throws IOException {
Ihab Awad26923222014-07-30 10:54:35 -0700415 if (o != null) {
416 serializer.startTag(null, CLASS_STATE);
Ihab Awadb78b2762014-07-25 15:16:23 -0700417
Ihab Awad26923222014-07-30 10:54:35 -0700418 if (o.defaultOutgoing != null) {
419 serializer.startTag(null, DEFAULT_OUTGOING);
420 sPhoneAccountHandleXml.writeToXml(o.defaultOutgoing, serializer);
421 serializer.endTag(null, DEFAULT_OUTGOING);
422 }
423
424 if (o.simCallManager != null) {
425 serializer.startTag(null, SIM_CALL_MANAGER);
426 sPhoneAccountHandleXml.writeToXml(o.simCallManager, serializer);
427 serializer.endTag(null, SIM_CALL_MANAGER);
428 }
429
430 serializer.startTag(null, ACCOUNTS);
431 for (PhoneAccount m : o.accounts) {
432 sPhoneAccountXml.writeToXml(m, serializer);
433 }
434 serializer.endTag(null, ACCOUNTS);
435
436 serializer.endTag(null, CLASS_STATE);
Ihab Awad293edf22014-07-24 17:52:29 -0700437 }
Ihab Awad104f8062014-07-17 11:29:35 -0700438 }
439
440 @Override
Ihab Awadb78b2762014-07-25 15:16:23 -0700441 public State readFromXml(XmlPullParser parser)
442 throws IOException, XmlPullParserException {
443 if (parser.getName().equals(CLASS_STATE)) {
444 State s = new State();
445 int outerDepth = parser.getDepth();
446 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
447 if (parser.getName().equals(DEFAULT_OUTGOING)) {
448 parser.nextTag();
449 s.defaultOutgoing = sPhoneAccountHandleXml.readFromXml(parser);
450 } else if (parser.getName().equals(SIM_CALL_MANAGER)) {
451 parser.nextTag();
452 s.simCallManager = sPhoneAccountHandleXml.readFromXml(parser);
453 } else if (parser.getName().equals(ACCOUNTS)) {
454 int accountsDepth = parser.getDepth();
455 while (XmlUtils.nextElementWithin(parser, accountsDepth)) {
456 PhoneAccount account = sPhoneAccountXml.readFromXml(parser);
457 if (account != null) {
458 s.accounts.add(account);
459 }
460 }
Ihab Awad104f8062014-07-17 11:29:35 -0700461 }
462 }
Ihab Awadb78b2762014-07-25 15:16:23 -0700463 return s;
Ihab Awad104f8062014-07-17 11:29:35 -0700464 }
Ihab Awadb78b2762014-07-25 15:16:23 -0700465 return null;
Ihab Awad104f8062014-07-17 11:29:35 -0700466 }
467 };
468
Ihab Awadb78b2762014-07-25 15:16:23 -0700469 @VisibleForTesting
470 public static final XmlSerialization<PhoneAccount> sPhoneAccountXml =
471 new XmlSerialization<PhoneAccount>() {
472 private static final String CLASS_PHONE_ACCOUNT = "phone_account";
473 private static final String ACCOUNT_HANDLE = "account_handle";
Ihab Awad104f8062014-07-17 11:29:35 -0700474 private static final String HANDLE = "handle";
Evan Charlton484f8d62014-07-18 14:06:58 -0700475 private static final String SUBSCRIPTION_NUMBER = "subscription_number";
Ihab Awad104f8062014-07-17 11:29:35 -0700476 private static final String CAPABILITIES = "capabilities";
477 private static final String ICON_RES_ID = "icon_res_id";
478 private static final String LABEL = "label";
479 private static final String SHORT_DESCRIPTION = "short_description";
Ihab Awad104f8062014-07-17 11:29:35 -0700480
481 @Override
Ihab Awadb78b2762014-07-25 15:16:23 -0700482 public void writeToXml(PhoneAccount o, XmlSerializer serializer)
483 throws IOException {
Ihab Awad26923222014-07-30 10:54:35 -0700484 if (o != null) {
485 serializer.startTag(null, CLASS_PHONE_ACCOUNT);
Ihab Awadb78b2762014-07-25 15:16:23 -0700486
Ihab Awad26923222014-07-30 10:54:35 -0700487 if (o.getAccountHandle() != null) {
488 serializer.startTag(null, ACCOUNT_HANDLE);
489 sPhoneAccountHandleXml.writeToXml(o.getAccountHandle(), serializer);
490 serializer.endTag(null, ACCOUNT_HANDLE);
491 }
Ihab Awadb78b2762014-07-25 15:16:23 -0700492
Ihab Awad26923222014-07-30 10:54:35 -0700493 writeTextSafely(HANDLE, o.getHandle(), serializer);
494 writeTextSafely(SUBSCRIPTION_NUMBER, o.getSubscriptionNumber(), serializer);
495 writeTextSafely(CAPABILITIES, Integer.toString(o.getCapabilities()), serializer);
496 writeTextSafely(ICON_RES_ID, Integer.toString(o.getIconResId()), serializer);
497 writeTextSafely(LABEL, o.getLabel(), serializer);
498 writeTextSafely(SHORT_DESCRIPTION, o.getShortDescription(), serializer);
Ihab Awadb78b2762014-07-25 15:16:23 -0700499
Ihab Awad26923222014-07-30 10:54:35 -0700500 serializer.endTag(null, CLASS_PHONE_ACCOUNT);
501 }
Ihab Awad104f8062014-07-17 11:29:35 -0700502 }
503
504 @Override
Ihab Awadb78b2762014-07-25 15:16:23 -0700505 public PhoneAccount readFromXml(XmlPullParser parser)
506 throws IOException, XmlPullParserException {
507 if (parser.getName().equals(CLASS_PHONE_ACCOUNT)) {
508 int outerDepth = parser.getDepth();
509 PhoneAccountHandle accountHandle = null;
510 Uri handle = null;
511 String subscriptionNumber = null;
512 int capabilities = 0;
513 int iconResId = 0;
514 String label = null;
515 String shortDescription = null;
516
517 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
518 if (parser.getName().equals(ACCOUNT_HANDLE)) {
519 parser.nextTag();
520 accountHandle = sPhoneAccountHandleXml.readFromXml(parser);
521 } else if (parser.getName().equals(HANDLE)) {
522 parser.next();
523 handle = Uri.parse(parser.getText());
524 } else if (parser.getName().equals(SUBSCRIPTION_NUMBER)) {
525 parser.next();
526 subscriptionNumber = parser.getText();
527 } else if (parser.getName().equals(CAPABILITIES)) {
528 parser.next();
529 capabilities = Integer.parseInt(parser.getText());
530 } else if (parser.getName().equals(ICON_RES_ID)) {
531 parser.next();
532 iconResId = Integer.parseInt(parser.getText());
533 } else if (parser.getName().equals(LABEL)) {
534 parser.next();
535 label = parser.getText();
536 } else if (parser.getName().equals(SHORT_DESCRIPTION)) {
537 parser.next();
538 shortDescription = parser.getText();
539 }
540 }
Ihab Awad26923222014-07-30 10:54:35 -0700541 return new PhoneAccount(
542 accountHandle,
543 handle,
544 subscriptionNumber,
545 capabilities,
546 iconResId,
547 label,
548 shortDescription);
Ihab Awadb78b2762014-07-25 15:16:23 -0700549 }
550 return null;
Ihab Awad104f8062014-07-17 11:29:35 -0700551 }
552 };
553
Ihab Awadb78b2762014-07-25 15:16:23 -0700554 @VisibleForTesting
555 public static final XmlSerialization<PhoneAccountHandle> sPhoneAccountHandleXml =
556 new XmlSerialization<PhoneAccountHandle>() {
557 private static final String CLASS_PHONE_ACCOUNT_HANDLE = "phone_account_handle";
Ihab Awad104f8062014-07-17 11:29:35 -0700558 private static final String COMPONENT_NAME = "component_name";
559 private static final String ID = "id";
560
561 @Override
Ihab Awadb78b2762014-07-25 15:16:23 -0700562 public void writeToXml(PhoneAccountHandle o, XmlSerializer serializer)
563 throws IOException {
Ihab Awad26923222014-07-30 10:54:35 -0700564 if (o != null) {
565 serializer.startTag(null, CLASS_PHONE_ACCOUNT_HANDLE);
Ihab Awadb78b2762014-07-25 15:16:23 -0700566
Ihab Awad26923222014-07-30 10:54:35 -0700567 if (o.getComponentName() != null) {
568 writeTextSafely(
569 COMPONENT_NAME, o.getComponentName().flattenToString(), serializer);
570 }
Ihab Awadb78b2762014-07-25 15:16:23 -0700571
Ihab Awad26923222014-07-30 10:54:35 -0700572 writeTextSafely(ID, o.getId(), serializer);
Ihab Awadb78b2762014-07-25 15:16:23 -0700573
Ihab Awad26923222014-07-30 10:54:35 -0700574 serializer.endTag(null, CLASS_PHONE_ACCOUNT_HANDLE);
575 }
Ihab Awad104f8062014-07-17 11:29:35 -0700576 }
577
578 @Override
Ihab Awadb78b2762014-07-25 15:16:23 -0700579 public PhoneAccountHandle readFromXml(XmlPullParser parser)
580 throws IOException, XmlPullParserException {
581 if (parser.getName().equals(CLASS_PHONE_ACCOUNT_HANDLE)) {
582 String componentNameString = null;
583 String idString = null;
584 int outerDepth = parser.getDepth();
585 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
586 if (parser.getName().equals(COMPONENT_NAME)) {
587 parser.next();
588 componentNameString = parser.getText();
589 } else if (parser.getName().equals(ID)) {
590 parser.next();
591 idString = parser.getText();
592 }
593 }
Ihab Awad26923222014-07-30 10:54:35 -0700594 if (componentNameString != null) {
Ihab Awadb78b2762014-07-25 15:16:23 -0700595 return new PhoneAccountHandle(
596 ComponentName.unflattenFromString(componentNameString),
597 idString);
598 }
599 }
600 return null;
Ihab Awad104f8062014-07-17 11:29:35 -0700601 }
602 };
Santos Cordon176ae282014-07-14 02:02:14 -0700603}