blob: c9e0e4658e29dbbe1773c4ad81c81b037597a00e [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 Charltonaf51ceb2014-07-30 11:56:36 -070019import android.content.Intent;
20import android.content.pm.PackageManager;
21import android.content.pm.ResolveInfo;
22import android.content.res.Resources;
23import android.telecomm.ConnectionService;
Evan Charlton94d01622014-07-20 12:32:05 -070024import android.telecomm.PhoneAccount;
Evan Charlton89176372014-07-19 18:23:09 -070025import android.telecomm.PhoneAccountHandle;
Santos Cordon176ae282014-07-14 02:02:14 -070026import android.content.ComponentName;
27import android.content.Context;
Santos Cordon176ae282014-07-14 02:02:14 -070028import android.net.Uri;
Ihab Awad104f8062014-07-17 11:29:35 -070029import android.telecomm.TelecommManager;
Evan Charltonaf51ceb2014-07-30 11:56:36 -070030import android.text.TextUtils;
Ihab Awadb78b2762014-07-25 15:16:23 -070031import android.util.AtomicFile;
32import android.util.Xml;
Santos Cordon176ae282014-07-14 02:02:14 -070033
Sailesh Nepal0e1dc5a2014-07-30 11:08:54 -070034import com.android.internal.annotations.VisibleForTesting;
35import com.android.internal.util.FastXmlSerializer;
36import com.android.internal.util.XmlUtils;
37
Evan Charltonaf51ceb2014-07-30 11:56:36 -070038import org.xmlpull.v1.XmlPullParser;
39import org.xmlpull.v1.XmlPullParserException;
40import org.xmlpull.v1.XmlSerializer;
41
Ihab Awadb78b2762014-07-25 15:16:23 -070042import java.io.BufferedInputStream;
43import java.io.BufferedOutputStream;
44import java.io.File;
45import java.io.FileNotFoundException;
46import java.io.FileOutputStream;
47import java.io.IOException;
48import java.io.InputStream;
Santos Cordon176ae282014-07-14 02:02:14 -070049import java.util.ArrayList;
50import java.util.List;
51import java.util.Objects;
Ihab Awadb78b2762014-07-25 15:16:23 -070052import java.util.concurrent.CopyOnWriteArrayList;
Santos Cordon176ae282014-07-14 02:02:14 -070053
54/**
Evan Charlton89176372014-07-19 18:23:09 -070055 * Handles writing and reading PhoneAccountHandle registration entries. This is a simple verbatim
Ihab Awad104f8062014-07-17 11:29:35 -070056 * delegate for all the account handling methods on {@link TelecommManager} as implemented in
57 * {@link TelecommServiceImpl}, with the notable exception that {@link TelecommServiceImpl} is
58 * responsible for security checking to make sure that the caller has proper authority over
Evan Charlton89176372014-07-19 18:23:09 -070059 * the {@code ComponentName}s they are declaring in their {@code PhoneAccountHandle}s.
Santos Cordon176ae282014-07-14 02:02:14 -070060 */
Ihab Awadb78b2762014-07-25 15:16:23 -070061public final class PhoneAccountRegistrar {
Santos Cordon176ae282014-07-14 02:02:14 -070062
Ihab Awadb78b2762014-07-25 15:16:23 -070063 public abstract static class Listener {
64 public void onAccountsChanged(PhoneAccountRegistrar registrar) {}
65 public void onDefaultOutgoingChanged(PhoneAccountRegistrar registrar) {}
66 public void onSimCallManagerChanged(PhoneAccountRegistrar registrar) {}
67 }
68
69 private static final String FILE_NAME = "phone-account-registrar-state.xml";
70
71 private final List<Listener> mListeners = new CopyOnWriteArrayList<>();
72 private final AtomicFile mAtomicFile;
73 private State mState;
Santos Cordon176ae282014-07-14 02:02:14 -070074
Ihab Awad26923222014-07-30 10:54:35 -070075 public PhoneAccountRegistrar(Context context) {
Ihab Awadb78b2762014-07-25 15:16:23 -070076 this(context, FILE_NAME);
77 }
78
79 @VisibleForTesting
80 public PhoneAccountRegistrar(Context context, String fileName) {
81 // TODO: Change file location when Telecomm is part of system
82 mAtomicFile = new AtomicFile(new File(context.getFilesDir(), fileName));
83 mState = new State();
84 read();
Santos Cordon176ae282014-07-14 02:02:14 -070085 }
86
Evan Charlton89176372014-07-19 18:23:09 -070087 public PhoneAccountHandle getDefaultOutgoingPhoneAccount() {
Ihab Awad293edf22014-07-24 17:52:29 -070088 if (mState.defaultOutgoing != null) {
Ihab Awadf2a84912014-07-22 21:09:25 -070089 // Return the registered outgoing default iff it still exists (we keep a sticky
90 // default to survive account deletion and re-addition)
Ihab Awad293edf22014-07-24 17:52:29 -070091 for (int i = 0; i < mState.accounts.size(); i++) {
92 if (mState.accounts.get(i).getAccountHandle().equals(mState.defaultOutgoing)) {
93 return mState.defaultOutgoing;
Ihab Awadf2a84912014-07-22 21:09:25 -070094 }
95 }
Ihab Awad293edf22014-07-24 17:52:29 -070096 // At this point, there was a registered default but it has been deleted; proceed
97 // as though there were no default
98 }
99
100 List<PhoneAccountHandle> enabled = getEnabledPhoneAccounts();
101 switch (enabled.size()) {
102 case 0:
103 // There are no accounts, so there can be no default
104 return null;
105 case 1:
106 // There is only one account, which is by definition the default
107 return enabled.get(0);
108 default:
109 // There are multiple accounts with no selected default
110 return null;
Ihab Awadf2a84912014-07-22 21:09:25 -0700111 }
Ihab Awad104f8062014-07-17 11:29:35 -0700112 }
Santos Cordon176ae282014-07-14 02:02:14 -0700113
Evan Charlton89176372014-07-19 18:23:09 -0700114 public void setDefaultOutgoingPhoneAccount(PhoneAccountHandle accountHandle) {
Evan Charlton89176372014-07-19 18:23:09 -0700115 if (accountHandle == null) {
Ihab Awad104f8062014-07-17 11:29:35 -0700116 // Asking to clear the default outgoing is a valid request
Ihab Awad293edf22014-07-24 17:52:29 -0700117 mState.defaultOutgoing = null;
Ihab Awad104f8062014-07-17 11:29:35 -0700118 } else {
119 boolean found = false;
Ihab Awad293edf22014-07-24 17:52:29 -0700120 for (PhoneAccount m : mState.accounts) {
Evan Charlton94d01622014-07-20 12:32:05 -0700121 if (Objects.equals(accountHandle, m.getAccountHandle())) {
Ihab Awad104f8062014-07-17 11:29:35 -0700122 found = true;
123 break;
124 }
Santos Cordon176ae282014-07-14 02:02:14 -0700125 }
Ihab Awad104f8062014-07-17 11:29:35 -0700126
127 if (!found) {
Ihab Awadb78b2762014-07-25 15:16:23 -0700128 Log.w(this, "Trying to set nonexistent default outgoing %s",
129 accountHandle);
130 return;
131 }
132
133 if (!has(getPhoneAccount(accountHandle), PhoneAccount.CAPABILITY_CALL_PROVIDER)) {
134 Log.w(this, "Trying to set non-call-provider default outgoing %s",
Evan Charlton89176372014-07-19 18:23:09 -0700135 accountHandle);
Ihab Awad104f8062014-07-17 11:29:35 -0700136 return;
137 }
138
Ihab Awad293edf22014-07-24 17:52:29 -0700139 mState.defaultOutgoing = accountHandle;
Santos Cordon176ae282014-07-14 02:02:14 -0700140 }
141
Ihab Awad293edf22014-07-24 17:52:29 -0700142 write();
Ihab Awadb78b2762014-07-25 15:16:23 -0700143 fireDefaultOutgoingChanged();
Santos Cordon176ae282014-07-14 02:02:14 -0700144 }
145
Ihab Awad293edf22014-07-24 17:52:29 -0700146 public void setSimCallManager(PhoneAccountHandle callManager) {
147 if (callManager != null) {
148 PhoneAccount callManagerAccount = getPhoneAccount(callManager);
149 if (callManagerAccount == null) {
150 Log.d(this, "setSimCallManager: Nonexistent call manager: %s", callManager);
151 return;
Ihab Awadb78b2762014-07-25 15:16:23 -0700152 } else if (!has(callManagerAccount, PhoneAccount.CAPABILITY_CONNECTION_MANAGER)) {
Ihab Awad293edf22014-07-24 17:52:29 -0700153 Log.d(this, "setSimCallManager: Not a call manager: %s", callManagerAccount);
154 return;
155 }
156 }
157 mState.simCallManager = callManager;
158 write();
Ihab Awadb78b2762014-07-25 15:16:23 -0700159 fireSimCallManagerChanged();
Ihab Awad293edf22014-07-24 17:52:29 -0700160 }
161
162 public PhoneAccountHandle getSimCallManager() {
Ihab Awadb78b2762014-07-25 15:16:23 -0700163 if (mState.simCallManager != null) {
164 // Return the registered sim call manager iff it still exists (we keep a sticky
165 // setting to survive account deletion and re-addition)
166 for (int i = 0; i < mState.accounts.size(); i++) {
167 if (mState.accounts.get(i).getAccountHandle().equals(mState.simCallManager)) {
168 return mState.simCallManager;
169 }
170 }
171 }
Evan Charltonaf51ceb2014-07-30 11:56:36 -0700172
173 // See if the OEM has specified a default one.
174 Context context = TelecommApp.getInstance();
175 String defaultConnectionMgr =
176 context.getResources().getString(R.string.default_connection_manager_component);
177 if (!TextUtils.isEmpty(defaultConnectionMgr)) {
178 PackageManager pm = context.getPackageManager();
179
180 ComponentName componentName = ComponentName.unflattenFromString(defaultConnectionMgr);
181 Intent intent = new Intent(ConnectionService.SERVICE_INTERFACE);
182 intent.setComponent(componentName);
183
184 // Make sure that the component can be resolved.
185 List<ResolveInfo> resolveInfos = pm.queryIntentServices(intent, 0);
186 if (!resolveInfos.isEmpty()) {
187 // See if there is registered PhoneAccount by this component.
188 List<PhoneAccountHandle> handles = getAllPhoneAccountHandles();
189 for (PhoneAccountHandle handle : handles) {
190 if (componentName.equals(handle.getComponentName())) {
191 return handle;
192 }
193 }
194 Log.d(this, "%s does not have a PhoneAccount; not using as default", componentName);
195 } else {
196 Log.d(this, "%s could not be resolved; not using as default", componentName);
197 }
198 } else {
199 Log.v(this, "No default connection manager specified");
200 }
201
Ihab Awadb78b2762014-07-25 15:16:23 -0700202 return null;
Ihab Awad293edf22014-07-24 17:52:29 -0700203 }
204
205 public List<PhoneAccountHandle> getAllPhoneAccountHandles() {
206 List<PhoneAccountHandle> accountHandles = new ArrayList<>();
207 for (PhoneAccount m : mState.accounts) {
208 accountHandles.add(m.getAccountHandle());
209 }
210 return accountHandles;
211 }
212
213 public List<PhoneAccount> getAllPhoneAccounts() {
214 return new ArrayList<>(mState.accounts);
215 }
216
217 // TODO: Rename systemwide to "getCallProviderPhoneAccounts"?
Evan Charlton89176372014-07-19 18:23:09 -0700218 public List<PhoneAccountHandle> getEnabledPhoneAccounts() {
Ihab Awad293edf22014-07-24 17:52:29 -0700219 return getCallProviderAccountHandles();
Santos Cordon176ae282014-07-14 02:02:14 -0700220 }
221
Ihab Awad293edf22014-07-24 17:52:29 -0700222 public PhoneAccount getPhoneAccount(PhoneAccountHandle handle) {
223 for (PhoneAccount m : mState.accounts) {
224 if (Objects.equals(handle, m.getAccountHandle())) {
Ihab Awad104f8062014-07-17 11:29:35 -0700225 return m;
Santos Cordon176ae282014-07-14 02:02:14 -0700226 }
227 }
228 return null;
229 }
230
Ihab Awad104f8062014-07-17 11:29:35 -0700231 // TODO: Should we implement an artificial limit for # of accounts associated with a single
232 // ComponentName?
Ihab Awad293edf22014-07-24 17:52:29 -0700233 public void registerPhoneAccount(PhoneAccount account) {
Ihab Awad293edf22014-07-24 17:52:29 -0700234 mState.accounts.add(account);
Ihab Awad104f8062014-07-17 11:29:35 -0700235 // Search for duplicates and remove any that are found.
Ihab Awad293edf22014-07-24 17:52:29 -0700236 for (int i = 0; i < mState.accounts.size() - 1; i++) {
237 if (Objects.equals(
238 account.getAccountHandle(), mState.accounts.get(i).getAccountHandle())) {
Ihab Awad104f8062014-07-17 11:29:35 -0700239 // replace existing entry.
Ihab Awad293edf22014-07-24 17:52:29 -0700240 mState.accounts.remove(i);
Ihab Awad104f8062014-07-17 11:29:35 -0700241 break;
Santos Cordon176ae282014-07-14 02:02:14 -0700242 }
243 }
244
Ihab Awad293edf22014-07-24 17:52:29 -0700245 write();
Ihab Awadb78b2762014-07-25 15:16:23 -0700246 fireAccountsChanged();
Ihab Awad293edf22014-07-24 17:52:29 -0700247 }
248
Evan Charlton89176372014-07-19 18:23:09 -0700249 public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) {
Ihab Awad293edf22014-07-24 17:52:29 -0700250 for (int i = 0; i < mState.accounts.size(); i++) {
251 if (Objects.equals(accountHandle, mState.accounts.get(i).getAccountHandle())) {
252 mState.accounts.remove(i);
Ihab Awad104f8062014-07-17 11:29:35 -0700253 break;
254 }
255 }
256
Ihab Awad293edf22014-07-24 17:52:29 -0700257 write();
Ihab Awadb78b2762014-07-25 15:16:23 -0700258 fireAccountsChanged();
Santos Cordon176ae282014-07-14 02:02:14 -0700259 }
260
Ihab Awad104f8062014-07-17 11:29:35 -0700261 public void clearAccounts(String packageName) {
Ihab Awad293edf22014-07-24 17:52:29 -0700262 for (int i = 0; i < mState.accounts.size(); i++) {
Ihab Awad104f8062014-07-17 11:29:35 -0700263 if (Objects.equals(
264 packageName,
Ihab Awad293edf22014-07-24 17:52:29 -0700265 mState.accounts.get(i).getAccountHandle()
266 .getComponentName().getPackageName())) {
267 mState.accounts.remove(i);
Ihab Awad104f8062014-07-17 11:29:35 -0700268 }
269 }
270
Ihab Awad293edf22014-07-24 17:52:29 -0700271 write();
Ihab Awadb78b2762014-07-25 15:16:23 -0700272 fireAccountsChanged();
273 }
274
275 public void addListener(Listener l) {
276 mListeners.add(l);
277 }
278
279 public void removeListener(Listener l) {
280 mListeners.remove(l);
281 }
282
283 private void fireAccountsChanged() {
284 for (Listener l : mListeners) {
285 l.onAccountsChanged(this);
286 }
287 }
288
289 private void fireDefaultOutgoingChanged() {
290 for (Listener l : mListeners) {
291 l.onDefaultOutgoingChanged(this);
292 }
293 }
294
295 private void fireSimCallManagerChanged() {
296 for (Listener l : mListeners) {
297 l.onSimCallManagerChanged(this);
298 }
Santos Cordon176ae282014-07-14 02:02:14 -0700299 }
300
Ihab Awad293edf22014-07-24 17:52:29 -0700301 ////////////////////////////////////////////////////////////////////////////////////////////////
302
303 // TODO: Add a corresponding has(...) method to class PhoneAccount itself and remove this one
304 // Return true iff the given account has all the specified capability flags
305 static boolean has(PhoneAccount account, int capability) {
306 return (account.getCapabilities() & capability) == capability;
307 }
308
309 private List<PhoneAccountHandle> getCallProviderAccountHandles() {
Evan Charlton94d01622014-07-20 12:32:05 -0700310 List<PhoneAccountHandle> accountHandles = new ArrayList<>();
Ihab Awad293edf22014-07-24 17:52:29 -0700311 for (PhoneAccount m : mState.accounts) {
312 if (has(m, PhoneAccount.CAPABILITY_CALL_PROVIDER)) {
Ihab Awadf2a84912014-07-22 21:09:25 -0700313 accountHandles.add(m.getAccountHandle());
314 }
Ihab Awad104f8062014-07-17 11:29:35 -0700315 }
Evan Charlton94d01622014-07-20 12:32:05 -0700316 return accountHandles;
Ihab Awad104f8062014-07-17 11:29:35 -0700317 }
318
Ihab Awad293edf22014-07-24 17:52:29 -0700319 /**
320 * The state of this {@code PhoneAccountRegistrar}.
321 */
Ihab Awadb78b2762014-07-25 15:16:23 -0700322 @VisibleForTesting
323 public static class State {
Ihab Awad293edf22014-07-24 17:52:29 -0700324 /**
325 * The account selected by the user to be employed by default for making outgoing calls.
326 * If the user has not made such a selection, then this is null.
327 */
328 public PhoneAccountHandle defaultOutgoing = null;
329
330 /**
Ihab Awadb78b2762014-07-25 15:16:23 -0700331 * A {@code PhoneAccount} having {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} which
Ihab Awad293edf22014-07-24 17:52:29 -0700332 * manages and optimizes a user's PSTN SIM connections.
333 */
334 public PhoneAccountHandle simCallManager;
335
336 /**
337 * The complete list of {@code PhoneAccount}s known to the Telecomm subsystem.
338 */
339 public final List<PhoneAccount> accounts = new ArrayList<>();
340 }
341
342 ////////////////////////////////////////////////////////////////////////////////////////////////
343 //
344 // State management
345 //
346
347 private void write() {
Ihab Awadb78b2762014-07-25 15:16:23 -0700348 final FileOutputStream os;
Ihab Awad104f8062014-07-17 11:29:35 -0700349 try {
Ihab Awadb78b2762014-07-25 15:16:23 -0700350 os = mAtomicFile.startWrite();
351 boolean success = false;
352 try {
353 XmlSerializer serializer = new FastXmlSerializer();
354 serializer.setOutput(new BufferedOutputStream(os), "utf-8");
355 writeToXml(mState, serializer);
356 serializer.flush();
357 success = true;
358 } finally {
359 if (success) {
360 mAtomicFile.finishWrite(os);
361 } else {
362 mAtomicFile.failWrite(os);
363 }
364 }
365 } catch (IOException e) {
366 Log.e(this, e, "Writing state to XML file");
Ihab Awad104f8062014-07-17 11:29:35 -0700367 }
368 }
369
Ihab Awadb78b2762014-07-25 15:16:23 -0700370 private void read() {
371 final InputStream is;
Ihab Awad104f8062014-07-17 11:29:35 -0700372 try {
Ihab Awadb78b2762014-07-25 15:16:23 -0700373 is = mAtomicFile.openRead();
374 } catch (FileNotFoundException ex) {
375 return;
376 }
377
378 XmlPullParser parser;
379 try {
380 parser = Xml.newPullParser();
381 parser.setInput(new BufferedInputStream(is), null);
382 parser.nextTag();
383 mState = readFromXml(parser);
384 } catch (IOException | XmlPullParserException e) {
385 Log.e(this, e, "Reading state from XML file");
386 mState = new State();
387 } finally {
388 try {
389 is.close();
390 } catch (IOException e) {
391 Log.e(this, e, "Closing InputStream");
392 }
Ihab Awad104f8062014-07-17 11:29:35 -0700393 }
Santos Cordon176ae282014-07-14 02:02:14 -0700394 }
395
Ihab Awadb78b2762014-07-25 15:16:23 -0700396 private static void writeToXml(State state, XmlSerializer serializer)
397 throws IOException {
398 sStateXml.writeToXml(state, serializer);
Santos Cordon176ae282014-07-14 02:02:14 -0700399 }
Ihab Awad104f8062014-07-17 11:29:35 -0700400
Ihab Awadb78b2762014-07-25 15:16:23 -0700401 private static State readFromXml(XmlPullParser parser)
402 throws IOException, XmlPullParserException {
403 State s = sStateXml.readFromXml(parser);
404 return s != null ? s : new State();
Ihab Awad104f8062014-07-17 11:29:35 -0700405 }
406
Ihab Awad293edf22014-07-24 17:52:29 -0700407 ////////////////////////////////////////////////////////////////////////////////////////////////
Ihab Awad104f8062014-07-17 11:29:35 -0700408 //
Ihab Awadb78b2762014-07-25 15:16:23 -0700409 // XML serialization
Ihab Awad104f8062014-07-17 11:29:35 -0700410 //
411
Ihab Awadb78b2762014-07-25 15:16:23 -0700412 @VisibleForTesting
Ihab Awad26923222014-07-30 10:54:35 -0700413 public abstract static class XmlSerialization<T> {
Ihab Awadb78b2762014-07-25 15:16:23 -0700414 /**
415 * Write the supplied object to XML
416 */
Ihab Awad26923222014-07-30 10:54:35 -0700417 public abstract void writeToXml(T o, XmlSerializer serializer)
418 throws IOException;
Ihab Awadb78b2762014-07-25 15:16:23 -0700419
420 /**
421 * Read from the supplied XML into a new object, returning null in case of an
422 * unrecoverable schema mismatch or other data error. 'parser' must be already
423 * positioned at the first tag that is expected to have been emitted by this
424 * object's writeToXml(). This object tries to fail early without modifying
425 * 'parser' if it does not recognize the data it sees.
426 */
Ihab Awad26923222014-07-30 10:54:35 -0700427 public abstract T readFromXml(XmlPullParser parser)
428 throws IOException, XmlPullParserException;
429
430 protected void writeTextSafely(String tagName, Object value, XmlSerializer serializer)
431 throws IOException {
432 if (value != null) {
433 serializer.startTag(null, tagName);
434 serializer.text(Objects.toString(value));
435 serializer.endTag(null, tagName);
436 }
437 }
Ihab Awad104f8062014-07-17 11:29:35 -0700438 }
439
Ihab Awadb78b2762014-07-25 15:16:23 -0700440 @VisibleForTesting
441 public static final XmlSerialization<State> sStateXml =
442 new XmlSerialization<State>() {
443 private static final String CLASS_STATE = "phone_account_registrar_state";
Ihab Awad104f8062014-07-17 11:29:35 -0700444 private static final String DEFAULT_OUTGOING = "default_outgoing";
Ihab Awad293edf22014-07-24 17:52:29 -0700445 private static final String SIM_CALL_MANAGER = "sim_call_manager";
Ihab Awad104f8062014-07-17 11:29:35 -0700446 private static final String ACCOUNTS = "accounts";
447
448 @Override
Ihab Awadb78b2762014-07-25 15:16:23 -0700449 public void writeToXml(State o, XmlSerializer serializer)
450 throws IOException {
Ihab Awad26923222014-07-30 10:54:35 -0700451 if (o != null) {
452 serializer.startTag(null, CLASS_STATE);
Ihab Awadb78b2762014-07-25 15:16:23 -0700453
Ihab Awad26923222014-07-30 10:54:35 -0700454 if (o.defaultOutgoing != null) {
455 serializer.startTag(null, DEFAULT_OUTGOING);
456 sPhoneAccountHandleXml.writeToXml(o.defaultOutgoing, serializer);
457 serializer.endTag(null, DEFAULT_OUTGOING);
458 }
459
460 if (o.simCallManager != null) {
461 serializer.startTag(null, SIM_CALL_MANAGER);
462 sPhoneAccountHandleXml.writeToXml(o.simCallManager, serializer);
463 serializer.endTag(null, SIM_CALL_MANAGER);
464 }
465
466 serializer.startTag(null, ACCOUNTS);
467 for (PhoneAccount m : o.accounts) {
468 sPhoneAccountXml.writeToXml(m, serializer);
469 }
470 serializer.endTag(null, ACCOUNTS);
471
472 serializer.endTag(null, CLASS_STATE);
Ihab Awad293edf22014-07-24 17:52:29 -0700473 }
Ihab Awad104f8062014-07-17 11:29:35 -0700474 }
475
476 @Override
Ihab Awadb78b2762014-07-25 15:16:23 -0700477 public State readFromXml(XmlPullParser parser)
478 throws IOException, XmlPullParserException {
479 if (parser.getName().equals(CLASS_STATE)) {
480 State s = new State();
481 int outerDepth = parser.getDepth();
482 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
483 if (parser.getName().equals(DEFAULT_OUTGOING)) {
484 parser.nextTag();
485 s.defaultOutgoing = sPhoneAccountHandleXml.readFromXml(parser);
486 } else if (parser.getName().equals(SIM_CALL_MANAGER)) {
487 parser.nextTag();
488 s.simCallManager = sPhoneAccountHandleXml.readFromXml(parser);
489 } else if (parser.getName().equals(ACCOUNTS)) {
490 int accountsDepth = parser.getDepth();
491 while (XmlUtils.nextElementWithin(parser, accountsDepth)) {
492 PhoneAccount account = sPhoneAccountXml.readFromXml(parser);
493 if (account != null) {
494 s.accounts.add(account);
495 }
496 }
Ihab Awad104f8062014-07-17 11:29:35 -0700497 }
498 }
Ihab Awadb78b2762014-07-25 15:16:23 -0700499 return s;
Ihab Awad104f8062014-07-17 11:29:35 -0700500 }
Ihab Awadb78b2762014-07-25 15:16:23 -0700501 return null;
Ihab Awad104f8062014-07-17 11:29:35 -0700502 }
503 };
504
Ihab Awadb78b2762014-07-25 15:16:23 -0700505 @VisibleForTesting
506 public static final XmlSerialization<PhoneAccount> sPhoneAccountXml =
507 new XmlSerialization<PhoneAccount>() {
508 private static final String CLASS_PHONE_ACCOUNT = "phone_account";
509 private static final String ACCOUNT_HANDLE = "account_handle";
Ihab Awad104f8062014-07-17 11:29:35 -0700510 private static final String HANDLE = "handle";
Evan Charlton484f8d62014-07-18 14:06:58 -0700511 private static final String SUBSCRIPTION_NUMBER = "subscription_number";
Ihab Awad104f8062014-07-17 11:29:35 -0700512 private static final String CAPABILITIES = "capabilities";
513 private static final String ICON_RES_ID = "icon_res_id";
514 private static final String LABEL = "label";
515 private static final String SHORT_DESCRIPTION = "short_description";
Ihab Awad104f8062014-07-17 11:29:35 -0700516
517 @Override
Ihab Awadb78b2762014-07-25 15:16:23 -0700518 public void writeToXml(PhoneAccount o, XmlSerializer serializer)
519 throws IOException {
Ihab Awad26923222014-07-30 10:54:35 -0700520 if (o != null) {
521 serializer.startTag(null, CLASS_PHONE_ACCOUNT);
Ihab Awadb78b2762014-07-25 15:16:23 -0700522
Ihab Awad26923222014-07-30 10:54:35 -0700523 if (o.getAccountHandle() != null) {
524 serializer.startTag(null, ACCOUNT_HANDLE);
525 sPhoneAccountHandleXml.writeToXml(o.getAccountHandle(), serializer);
526 serializer.endTag(null, ACCOUNT_HANDLE);
527 }
Ihab Awadb78b2762014-07-25 15:16:23 -0700528
Ihab Awad26923222014-07-30 10:54:35 -0700529 writeTextSafely(HANDLE, o.getHandle(), serializer);
530 writeTextSafely(SUBSCRIPTION_NUMBER, o.getSubscriptionNumber(), serializer);
531 writeTextSafely(CAPABILITIES, Integer.toString(o.getCapabilities()), serializer);
532 writeTextSafely(ICON_RES_ID, Integer.toString(o.getIconResId()), serializer);
533 writeTextSafely(LABEL, o.getLabel(), serializer);
534 writeTextSafely(SHORT_DESCRIPTION, o.getShortDescription(), serializer);
Ihab Awadb78b2762014-07-25 15:16:23 -0700535
Ihab Awad26923222014-07-30 10:54:35 -0700536 serializer.endTag(null, CLASS_PHONE_ACCOUNT);
537 }
Ihab Awad104f8062014-07-17 11:29:35 -0700538 }
539
540 @Override
Ihab Awadb78b2762014-07-25 15:16:23 -0700541 public PhoneAccount readFromXml(XmlPullParser parser)
542 throws IOException, XmlPullParserException {
543 if (parser.getName().equals(CLASS_PHONE_ACCOUNT)) {
544 int outerDepth = parser.getDepth();
545 PhoneAccountHandle accountHandle = null;
546 Uri handle = null;
547 String subscriptionNumber = null;
548 int capabilities = 0;
549 int iconResId = 0;
550 String label = null;
551 String shortDescription = null;
552
553 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
554 if (parser.getName().equals(ACCOUNT_HANDLE)) {
555 parser.nextTag();
556 accountHandle = sPhoneAccountHandleXml.readFromXml(parser);
557 } else if (parser.getName().equals(HANDLE)) {
558 parser.next();
559 handle = Uri.parse(parser.getText());
560 } else if (parser.getName().equals(SUBSCRIPTION_NUMBER)) {
561 parser.next();
562 subscriptionNumber = parser.getText();
563 } else if (parser.getName().equals(CAPABILITIES)) {
564 parser.next();
565 capabilities = Integer.parseInt(parser.getText());
566 } else if (parser.getName().equals(ICON_RES_ID)) {
567 parser.next();
568 iconResId = Integer.parseInt(parser.getText());
569 } else if (parser.getName().equals(LABEL)) {
570 parser.next();
571 label = parser.getText();
572 } else if (parser.getName().equals(SHORT_DESCRIPTION)) {
573 parser.next();
574 shortDescription = parser.getText();
575 }
576 }
Ihab Awad26923222014-07-30 10:54:35 -0700577 return new PhoneAccount(
578 accountHandle,
579 handle,
580 subscriptionNumber,
581 capabilities,
582 iconResId,
583 label,
584 shortDescription);
Ihab Awadb78b2762014-07-25 15:16:23 -0700585 }
586 return null;
Ihab Awad104f8062014-07-17 11:29:35 -0700587 }
588 };
589
Ihab Awadb78b2762014-07-25 15:16:23 -0700590 @VisibleForTesting
591 public static final XmlSerialization<PhoneAccountHandle> sPhoneAccountHandleXml =
592 new XmlSerialization<PhoneAccountHandle>() {
593 private static final String CLASS_PHONE_ACCOUNT_HANDLE = "phone_account_handle";
Ihab Awad104f8062014-07-17 11:29:35 -0700594 private static final String COMPONENT_NAME = "component_name";
595 private static final String ID = "id";
596
597 @Override
Ihab Awadb78b2762014-07-25 15:16:23 -0700598 public void writeToXml(PhoneAccountHandle o, XmlSerializer serializer)
599 throws IOException {
Ihab Awad26923222014-07-30 10:54:35 -0700600 if (o != null) {
601 serializer.startTag(null, CLASS_PHONE_ACCOUNT_HANDLE);
Ihab Awadb78b2762014-07-25 15:16:23 -0700602
Ihab Awad26923222014-07-30 10:54:35 -0700603 if (o.getComponentName() != null) {
604 writeTextSafely(
605 COMPONENT_NAME, o.getComponentName().flattenToString(), serializer);
606 }
Ihab Awadb78b2762014-07-25 15:16:23 -0700607
Ihab Awad26923222014-07-30 10:54:35 -0700608 writeTextSafely(ID, o.getId(), serializer);
Ihab Awadb78b2762014-07-25 15:16:23 -0700609
Ihab Awad26923222014-07-30 10:54:35 -0700610 serializer.endTag(null, CLASS_PHONE_ACCOUNT_HANDLE);
611 }
Ihab Awad104f8062014-07-17 11:29:35 -0700612 }
613
614 @Override
Ihab Awadb78b2762014-07-25 15:16:23 -0700615 public PhoneAccountHandle readFromXml(XmlPullParser parser)
616 throws IOException, XmlPullParserException {
617 if (parser.getName().equals(CLASS_PHONE_ACCOUNT_HANDLE)) {
618 String componentNameString = null;
619 String idString = null;
620 int outerDepth = parser.getDepth();
621 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
622 if (parser.getName().equals(COMPONENT_NAME)) {
623 parser.next();
624 componentNameString = parser.getText();
625 } else if (parser.getName().equals(ID)) {
626 parser.next();
627 idString = parser.getText();
628 }
629 }
Ihab Awad26923222014-07-30 10:54:35 -0700630 if (componentNameString != null) {
Ihab Awadb78b2762014-07-25 15:16:23 -0700631 return new PhoneAccountHandle(
632 ComponentName.unflattenFromString(componentNameString),
633 idString);
634 }
635 }
636 return null;
Ihab Awad104f8062014-07-17 11:29:35 -0700637 }
638 };
Santos Cordon176ae282014-07-14 02:02:14 -0700639}