blob: eb3248e60d5ae95dbafa547df197c78934ffc124 [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
Tyler Gunn7cc70b42014-09-12 22:17:27 -070017package com.android.server.telecom;
Santos Cordon176ae282014-07-14 02:02:14 -070018
Tyler Gunncb59b672014-08-20 09:02:11 -070019import android.Manifest;
Evan Charlton105d9772014-11-25 14:08:53 -080020import android.content.ComponentName;
21import android.content.Context;
Evan Charltonaf51ceb2014-07-30 11:56:36 -070022import android.content.Intent;
23import android.content.pm.PackageManager;
24import android.content.pm.ResolveInfo;
Tyler Gunncb59b672014-08-20 09:02:11 -070025import android.content.pm.ServiceInfo;
Evan Charlton105d9772014-11-25 14:08:53 -080026import android.content.pm.UserInfo;
Ihab Awadd9f54382014-10-24 11:44:47 -070027import android.graphics.Bitmap;
28import android.graphics.BitmapFactory;
Evan Charlton105d9772014-11-25 14:08:53 -080029import android.net.Uri;
30import android.os.Process;
31import android.os.UserHandle;
32import android.os.UserManager;
Tyler Gunn84253572014-09-02 14:50:05 -070033import android.provider.Settings;
Tyler Gunn7cc70b42014-09-12 22:17:27 -070034import android.telecom.ConnectionService;
35import android.telecom.PhoneAccount;
36import android.telecom.PhoneAccountHandle;
Nancy Chen140004a2014-10-15 15:48:38 -070037import android.telephony.PhoneNumberUtils;
Nancy Chen5a36b6e2014-10-23 17:42:42 -070038import android.telephony.SubscriptionManager;
Evan Charltonaf51ceb2014-07-30 11:56:36 -070039import android.text.TextUtils;
Ihab Awadb78b2762014-07-25 15:16:23 -070040import android.util.AtomicFile;
Ihab Awadd9f54382014-10-24 11:44:47 -070041import android.util.Base64;
Ihab Awadb78b2762014-07-25 15:16:23 -070042import android.util.Xml;
Santos Cordon176ae282014-07-14 02:02:14 -070043
Tyler Gunn91d43cf2014-09-17 12:19:39 -070044// TODO: Needed for move to system service: import com.android.internal.R;
Sailesh Nepal0e1dc5a2014-07-30 11:08:54 -070045import com.android.internal.annotations.VisibleForTesting;
46import com.android.internal.util.FastXmlSerializer;
Tyler Gunn9787e0e2014-10-14 14:36:12 -070047import com.android.internal.util.IndentingPrintWriter;
Sailesh Nepal0e1dc5a2014-07-30 11:08:54 -070048import com.android.internal.util.XmlUtils;
49
Evan Charltonaf51ceb2014-07-30 11:56:36 -070050import org.xmlpull.v1.XmlPullParser;
51import org.xmlpull.v1.XmlPullParserException;
52import org.xmlpull.v1.XmlSerializer;
53
Ihab Awadb78b2762014-07-25 15:16:23 -070054import java.io.BufferedInputStream;
55import java.io.BufferedOutputStream;
Ihab Awadd9f54382014-10-24 11:44:47 -070056import java.io.ByteArrayOutputStream;
Ihab Awadb78b2762014-07-25 15:16:23 -070057import java.io.File;
58import java.io.FileNotFoundException;
59import java.io.FileOutputStream;
60import java.io.IOException;
61import java.io.InputStream;
Tyler Gunn84253572014-09-02 14:50:05 -070062import java.lang.Integer;
Tyler Gunncb59b672014-08-20 09:02:11 -070063import java.lang.SecurityException;
Tyler Gunnd900ce62014-08-13 11:40:59 -070064import java.lang.String;
Santos Cordon176ae282014-07-14 02:02:14 -070065import java.util.ArrayList;
Tyler Gunnd900ce62014-08-13 11:40:59 -070066import java.util.Iterator;
Santos Cordon176ae282014-07-14 02:02:14 -070067import java.util.List;
68import java.util.Objects;
Ihab Awadb78b2762014-07-25 15:16:23 -070069import java.util.concurrent.CopyOnWriteArrayList;
Santos Cordon176ae282014-07-14 02:02:14 -070070
71/**
Evan Charlton89176372014-07-19 18:23:09 -070072 * Handles writing and reading PhoneAccountHandle registration entries. This is a simple verbatim
Tyler Gunn7cc70b42014-09-12 22:17:27 -070073 * delegate for all the account handling methods on {@link android.telecom.TelecomManager} as implemented in
74 * {@link TelecomServiceImpl}, with the notable exception that {@link TelecomServiceImpl} is
Ihab Awad104f8062014-07-17 11:29:35 -070075 * responsible for security checking to make sure that the caller has proper authority over
Evan Charlton89176372014-07-19 18:23:09 -070076 * the {@code ComponentName}s they are declaring in their {@code PhoneAccountHandle}s.
Santos Cordon176ae282014-07-14 02:02:14 -070077 */
Ihab Awadb78b2762014-07-25 15:16:23 -070078public final class PhoneAccountRegistrar {
Santos Cordon176ae282014-07-14 02:02:14 -070079
Yorke Lee5e8836a2014-08-22 15:25:18 -070080 public static final PhoneAccountHandle NO_ACCOUNT_SELECTED =
81 new PhoneAccountHandle(new ComponentName("null", "null"), "NO_ACCOUNT_SELECTED");
82
Ihab Awadb78b2762014-07-25 15:16:23 -070083 public abstract static class Listener {
84 public void onAccountsChanged(PhoneAccountRegistrar registrar) {}
85 public void onDefaultOutgoingChanged(PhoneAccountRegistrar registrar) {}
86 public void onSimCallManagerChanged(PhoneAccountRegistrar registrar) {}
87 }
88
89 private static final String FILE_NAME = "phone-account-registrar-state.xml";
Tyler Gunn84253572014-09-02 14:50:05 -070090 @VisibleForTesting
Ihab Awad7e2c7f32014-11-03 09:49:45 -080091 public static final int EXPECTED_STATE_VERSION = 5;
Tyler Gunn84253572014-09-02 14:50:05 -070092
93 /** Keep in sync with the same in SipSettings.java */
94 private static final String SIP_SHARED_PREFERENCES = "SIP_PREFERENCES";
Ihab Awadb78b2762014-07-25 15:16:23 -070095
96 private final List<Listener> mListeners = new CopyOnWriteArrayList<>();
97 private final AtomicFile mAtomicFile;
Santos Cordonafe59e52014-08-22 16:48:43 -070098 private final Context mContext;
Evan Charlton105d9772014-11-25 14:08:53 -080099 private final UserManager mUserManager;
Wink Saville33c05d32014-11-20 13:04:17 -0800100 private final SubscriptionManager mSubscriptionManager;
Ihab Awadb78b2762014-07-25 15:16:23 -0700101 private State mState;
Evan Charlton105d9772014-11-25 14:08:53 -0800102 private UserHandle mCurrentUserHandle;
Santos Cordon176ae282014-07-14 02:02:14 -0700103
Nancy Chen06ce0622014-10-23 01:17:35 +0000104 @VisibleForTesting
Ihab Awad26923222014-07-30 10:54:35 -0700105 public PhoneAccountRegistrar(Context context) {
Ihab Awadb78b2762014-07-25 15:16:23 -0700106 this(context, FILE_NAME);
107 }
108
109 @VisibleForTesting
110 public PhoneAccountRegistrar(Context context, String fileName) {
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700111 // TODO: This file path is subject to change -- it is storing the phone account registry
112 // state file in the path /data/system/users/0/, which is likely not correct in a
113 // multi-user setting.
114 /** UNCOMMENT_FOR_MOVE_TO_SYSTEM_SERVICE
115 String filePath = Environment.getUserSystemDirectory(UserHandle.myUserId()).
116 getAbsolutePath();
117 mAtomicFile = new AtomicFile(new File(filePath, fileName));
118 UNCOMMENT_FOR_MOVE_TO_SYSTEM_SERVICE */
Ihab Awadb78b2762014-07-25 15:16:23 -0700119 mAtomicFile = new AtomicFile(new File(context.getFilesDir(), fileName));
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700120
Ihab Awadb78b2762014-07-25 15:16:23 -0700121 mState = new State();
Santos Cordonafe59e52014-08-22 16:48:43 -0700122 mContext = context;
Evan Charlton105d9772014-11-25 14:08:53 -0800123 mUserManager = UserManager.get(context);
Wink Saville33c05d32014-11-20 13:04:17 -0800124 mSubscriptionManager = SubscriptionManager.from(mContext);
Evan Charlton105d9772014-11-25 14:08:53 -0800125 mCurrentUserHandle = Process.myUserHandle();
Ihab Awadb78b2762014-07-25 15:16:23 -0700126 read();
Santos Cordon176ae282014-07-14 02:02:14 -0700127 }
128
Tyler Gunn84253572014-09-02 14:50:05 -0700129 /**
Nancy Chen140004a2014-10-15 15:48:38 -0700130 * Retrieves the subscription id for a given phone account if it exists. Subscription ids
131 * apply only to PSTN/SIM card phone accounts so all other accounts should not have a
132 * subscription id.
133 * @param accountHandle The handle for the phone account for which to retrieve the
134 * subscription id.
Wink Saville35850602014-10-23 15:57:21 -0700135 * @return The value of the subscription id or -1 if it does not exist or is not valid.
Nancy Chen140004a2014-10-15 15:48:38 -0700136 */
Wink Saville35850602014-10-23 15:57:21 -0700137 public int getSubscriptionIdForPhoneAccount(PhoneAccountHandle accountHandle) {
Evan Charlton105d9772014-11-25 14:08:53 -0800138 PhoneAccount account = getPhoneAccountInternal(accountHandle);
139 if (account == null
140 || !account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
141 || !TextUtils.isDigitsOnly(accountHandle.getId())
142 || !isVisibleForUser(accountHandle)) {
Nancy Chen140004a2014-10-15 15:48:38 -0700143 // Since no decimals or negative numbers can be valid subscription ids, only a string of
144 // numbers can be subscription id
Wink Saville33c05d32014-11-20 13:04:17 -0800145 return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
Nancy Chen140004a2014-10-15 15:48:38 -0700146 }
Wink Saville35850602014-10-23 15:57:21 -0700147 return Integer.parseInt(accountHandle.getId());
Nancy Chen140004a2014-10-15 15:48:38 -0700148 }
149
150 /**
Evan Charlton105d9772014-11-25 14:08:53 -0800151 * Retrieves the default outgoing phone account supporting the specified uriScheme. Note that if
152 * {@link #mCurrentUserHandle} does not have visibility into the current default, {@code null}
153 * will be returned.
154 *
Tyler Gunn84253572014-09-02 14:50:05 -0700155 * @param uriScheme The URI scheme for the outgoing call.
156 * @return The {@link PhoneAccountHandle} to use.
157 */
158 public PhoneAccountHandle getDefaultOutgoingPhoneAccount(String uriScheme) {
Yorke Lee5e8836a2014-08-22 15:25:18 -0700159 final PhoneAccountHandle userSelected = getUserSelectedOutgoingPhoneAccount();
Tyler Gunn84253572014-09-02 14:50:05 -0700160
Yorke Lee5e8836a2014-08-22 15:25:18 -0700161 if (userSelected != null) {
Tyler Gunn84253572014-09-02 14:50:05 -0700162 // If there is a default PhoneAccount, ensure it supports calls to handles with the
163 // specified uriScheme.
Evan Charlton105d9772014-11-25 14:08:53 -0800164 final PhoneAccount userSelectedAccount = getPhoneAccountInternal(userSelected);
165 if (userSelectedAccount.supportsUriScheme(uriScheme)
166 && isVisibleForUser(userSelected)) {
Tyler Gunn84253572014-09-02 14:50:05 -0700167 return userSelected;
168 }
Ihab Awad293edf22014-07-24 17:52:29 -0700169 }
170
Nancy Chen309198e2014-09-15 18:02:49 -0700171 List<PhoneAccountHandle> outgoing = getCallCapablePhoneAccounts(uriScheme);
Ihab Awad6fb37c82014-08-07 19:48:57 -0700172 switch (outgoing.size()) {
Ihab Awad293edf22014-07-24 17:52:29 -0700173 case 0:
174 // There are no accounts, so there can be no default
175 return null;
176 case 1:
Evan Charlton105d9772014-11-25 14:08:53 -0800177 // There is only one account, which is by definition the default.
178 PhoneAccountHandle onlyHandle = outgoing.get(0);
179 if (isVisibleForUser(onlyHandle)) {
180 return outgoing.get(0);
181 }
182 return null;
Ihab Awad293edf22014-07-24 17:52:29 -0700183 default:
184 // There are multiple accounts with no selected default
185 return null;
Ihab Awadf2a84912014-07-22 21:09:25 -0700186 }
Ihab Awad104f8062014-07-17 11:29:35 -0700187 }
Santos Cordon176ae282014-07-14 02:02:14 -0700188
Evan Charlton105d9772014-11-25 14:08:53 -0800189 /**
190 * @return The user-selected outgoing {@link PhoneAccount}, or null if it hasn't been set (or
191 * if it was set by another user).
192 */
Yorke Lee5e8836a2014-08-22 15:25:18 -0700193 PhoneAccountHandle getUserSelectedOutgoingPhoneAccount() {
194 if (mState.defaultOutgoing != null) {
195 // Return the registered outgoing default iff it still exists (we keep a sticky
196 // default to survive account deletion and re-addition)
197 for (int i = 0; i < mState.accounts.size(); i++) {
Evan Charlton105d9772014-11-25 14:08:53 -0800198 if (mState.accounts.get(i).getAccountHandle().equals(mState.defaultOutgoing)
199 && isVisibleForUser(mState.defaultOutgoing)) {
Yorke Lee5e8836a2014-08-22 15:25:18 -0700200 return mState.defaultOutgoing;
201 }
202 }
203 // At this point, there was a registered default but it has been deleted; proceed
204 // as though there were no default
205 }
206 return null;
207 }
208
Andrew Leea51a3862014-09-03 14:58:45 -0700209 public void setUserSelectedOutgoingPhoneAccount(PhoneAccountHandle accountHandle) {
Evan Charlton89176372014-07-19 18:23:09 -0700210 if (accountHandle == null) {
Ihab Awad104f8062014-07-17 11:29:35 -0700211 // Asking to clear the default outgoing is a valid request
Ihab Awad293edf22014-07-24 17:52:29 -0700212 mState.defaultOutgoing = null;
Ihab Awad104f8062014-07-17 11:29:35 -0700213 } else {
214 boolean found = false;
Ihab Awad293edf22014-07-24 17:52:29 -0700215 for (PhoneAccount m : mState.accounts) {
Evan Charlton94d01622014-07-20 12:32:05 -0700216 if (Objects.equals(accountHandle, m.getAccountHandle())) {
Ihab Awad104f8062014-07-17 11:29:35 -0700217 found = true;
218 break;
219 }
Santos Cordon176ae282014-07-14 02:02:14 -0700220 }
Ihab Awad104f8062014-07-17 11:29:35 -0700221
222 if (!found) {
Ihab Awadb78b2762014-07-25 15:16:23 -0700223 Log.w(this, "Trying to set nonexistent default outgoing %s",
224 accountHandle);
225 return;
226 }
227
Evan Charlton105d9772014-11-25 14:08:53 -0800228 if (!getPhoneAccountInternal(accountHandle).hasCapabilities(
Tyler Gunn8e0fef42014-09-08 18:34:44 -0700229 PhoneAccount.CAPABILITY_CALL_PROVIDER)) {
Ihab Awadb78b2762014-07-25 15:16:23 -0700230 Log.w(this, "Trying to set non-call-provider default outgoing %s",
Evan Charlton89176372014-07-19 18:23:09 -0700231 accountHandle);
Ihab Awad104f8062014-07-17 11:29:35 -0700232 return;
233 }
234
Evan Charlton105d9772014-11-25 14:08:53 -0800235 if (getPhoneAccountInternal(accountHandle).hasCapabilities(
Nancy Chen5a36b6e2014-10-23 17:42:42 -0700236 PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
237 // If the account selected is a SIM account, propagate down to the subscription
238 // record.
Wink Saville7ce6e782014-10-27 10:56:46 -0700239 int subId = getSubscriptionIdForPhoneAccount(accountHandle);
Wink Saville33c05d32014-11-20 13:04:17 -0800240 mSubscriptionManager.setDefaultVoiceSubId(subId);
Nancy Chen5a36b6e2014-10-23 17:42:42 -0700241 }
242
Ihab Awad293edf22014-07-24 17:52:29 -0700243 mState.defaultOutgoing = accountHandle;
Santos Cordon176ae282014-07-14 02:02:14 -0700244 }
245
Ihab Awad293edf22014-07-24 17:52:29 -0700246 write();
Ihab Awadb78b2762014-07-25 15:16:23 -0700247 fireDefaultOutgoingChanged();
Santos Cordon176ae282014-07-14 02:02:14 -0700248 }
249
Nancy Chen668dee02014-11-19 15:31:31 -0800250 boolean isUserSelectedSmsPhoneAccount(PhoneAccountHandle accountHandle) {
251 return getSubscriptionIdForPhoneAccount(accountHandle) ==
252 SubscriptionManager.getDefaultSmsSubId();
253 }
254
Ihab Awad293edf22014-07-24 17:52:29 -0700255 public void setSimCallManager(PhoneAccountHandle callManager) {
256 if (callManager != null) {
Evan Charlton105d9772014-11-25 14:08:53 -0800257 PhoneAccount callManagerAccount = getPhoneAccountInternal(callManager);
Ihab Awad293edf22014-07-24 17:52:29 -0700258 if (callManagerAccount == null) {
259 Log.d(this, "setSimCallManager: Nonexistent call manager: %s", callManager);
260 return;
Tyler Gunn8e0fef42014-09-08 18:34:44 -0700261 } else if (!callManagerAccount.hasCapabilities(
262 PhoneAccount.CAPABILITY_CONNECTION_MANAGER)) {
Ihab Awad293edf22014-07-24 17:52:29 -0700263 Log.d(this, "setSimCallManager: Not a call manager: %s", callManagerAccount);
264 return;
265 }
Yorke Lee5e8836a2014-08-22 15:25:18 -0700266 } else {
267 callManager = NO_ACCOUNT_SELECTED;
Ihab Awad293edf22014-07-24 17:52:29 -0700268 }
269 mState.simCallManager = callManager;
Yorke Lee5e8836a2014-08-22 15:25:18 -0700270
Ihab Awad293edf22014-07-24 17:52:29 -0700271 write();
Ihab Awadb78b2762014-07-25 15:16:23 -0700272 fireSimCallManagerChanged();
Ihab Awad293edf22014-07-24 17:52:29 -0700273 }
274
Evan Charlton105d9772014-11-25 14:08:53 -0800275 /**
276 * @return The {@link PhoneAccount}s which are visible to {@link #mCurrentUserHandle}.
277 */
Ihab Awad293edf22014-07-24 17:52:29 -0700278 public PhoneAccountHandle getSimCallManager() {
Ihab Awadb78b2762014-07-25 15:16:23 -0700279 if (mState.simCallManager != null) {
Yorke Lee5e8836a2014-08-22 15:25:18 -0700280 if (NO_ACCOUNT_SELECTED.equals(mState.simCallManager)) {
281 return null;
282 }
Ihab Awadb78b2762014-07-25 15:16:23 -0700283 // Return the registered sim call manager iff it still exists (we keep a sticky
284 // setting to survive account deletion and re-addition)
285 for (int i = 0; i < mState.accounts.size(); i++) {
mike dooley10a58312014-11-06 13:46:19 -0800286 if (mState.accounts.get(i).getAccountHandle().equals(mState.simCallManager)
Evan Charlton105d9772014-11-25 14:08:53 -0800287 && !resolveComponent(mState.simCallManager).isEmpty()
288 && isVisibleForUser(mState.simCallManager)) {
Ihab Awadb78b2762014-07-25 15:16:23 -0700289 return mState.simCallManager;
290 }
291 }
292 }
Evan Charltonaf51ceb2014-07-30 11:56:36 -0700293
294 // See if the OEM has specified a default one.
Evan Charltonaf51ceb2014-07-30 11:56:36 -0700295 String defaultConnectionMgr =
Santos Cordonafe59e52014-08-22 16:48:43 -0700296 mContext.getResources().getString(R.string.default_connection_manager_component);
Evan Charltonaf51ceb2014-07-30 11:56:36 -0700297 if (!TextUtils.isEmpty(defaultConnectionMgr)) {
Evan Charltonaf51ceb2014-07-30 11:56:36 -0700298 ComponentName componentName = ComponentName.unflattenFromString(defaultConnectionMgr);
Evan Charltonaf51ceb2014-07-30 11:56:36 -0700299 // Make sure that the component can be resolved.
Evan Charlton105d9772014-11-25 14:08:53 -0800300 List<ResolveInfo> resolveInfos = resolveComponent(componentName, null);
Evan Charltonaf51ceb2014-07-30 11:56:36 -0700301 if (!resolveInfos.isEmpty()) {
302 // See if there is registered PhoneAccount by this component.
303 List<PhoneAccountHandle> handles = getAllPhoneAccountHandles();
304 for (PhoneAccountHandle handle : handles) {
Evan Charlton105d9772014-11-25 14:08:53 -0800305 if (componentName.equals(handle.getComponentName())
306 && isVisibleForUser(handle)) {
Evan Charltonaf51ceb2014-07-30 11:56:36 -0700307 return handle;
308 }
309 }
310 Log.d(this, "%s does not have a PhoneAccount; not using as default", componentName);
311 } else {
312 Log.d(this, "%s could not be resolved; not using as default", componentName);
313 }
314 } else {
315 Log.v(this, "No default connection manager specified");
316 }
317
Ihab Awadb78b2762014-07-25 15:16:23 -0700318 return null;
Ihab Awad293edf22014-07-24 17:52:29 -0700319 }
320
Evan Charlton105d9772014-11-25 14:08:53 -0800321 /**
322 * A version of {@link #getPhoneAccount} which does not guard for the current user.
323 *
324 * @param handle
325 * @return
326 */
327 PhoneAccount getPhoneAccountInternal(PhoneAccountHandle handle) {
328 for (PhoneAccount m : mState.accounts) {
329 if (Objects.equals(handle, m.getAccountHandle())) {
330 return m;
331 }
332 }
333 return null;
334 }
335
336 /**
337 * Update the current UserHandle to track when users are switched. This will allow the
338 * PhoneAccountRegistar to self-filter the PhoneAccounts to make sure we don't leak anything
339 * across users.
340 *
341 * @param userHandle The {@link UserHandle}, as delivered by
342 * {@link Intent#ACTION_USER_SWITCHED}.
343 */
344 public void setCurrentUserHandle(UserHandle userHandle) {
345 if (userHandle == null) {
346 Log.d(this, "setCurrentUserHandle, userHandle = null");
347 userHandle = Process.myUserHandle();
348 }
349 Log.d(this, "setCurrentUserHandle, %s", userHandle);
350 mCurrentUserHandle = userHandle;
351 }
352
353 private boolean isVisibleForUser(PhoneAccountHandle accountHandle) {
354 if (accountHandle == null) {
355 return false;
356 }
357
358 return isVisibleForUser(getPhoneAccountInternal(accountHandle));
359 }
360
361 private boolean isVisibleForUser(PhoneAccount account) {
362 if (account == null) {
363 return false;
364 }
365
366 // If this PhoneAccount has CAPABILITY_MULTI_USER, it should be visible to all users and
367 // all profiles. Only Telephony and SIP accounts should have this capability.
368 if (account.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
369 return true;
370 }
371
372 UserHandle phoneAccountUserHandle = account.getAccountHandle().getUserHandle();
373 if (phoneAccountUserHandle == null) {
374 return false;
375 }
376
377 if (mCurrentUserHandle == null) {
378 Log.d(this, "Current user is null; assuming true");
379 return true;
380 }
381
382 // Unlike in TelecomServiceImpl, we only care about *profiles* here. We want to make sure
383 // that we don't resolve PhoneAccount across *users*, but resolving across *profiles* is
384 // fine.
385 List<UserInfo> profileUsers = mUserManager.getProfiles(mCurrentUserHandle.getIdentifier());
386
387 for (UserInfo profileInfo : profileUsers) {
388 if (profileInfo.getUserHandle().equals(phoneAccountUserHandle)) {
389 return true;
390 }
391 }
392 return false;
393 }
394
395 private List<ResolveInfo> resolveComponent(PhoneAccountHandle phoneAccountHandle) {
396 return resolveComponent(phoneAccountHandle.getComponentName(),
397 phoneAccountHandle.getUserHandle());
398 }
399
400 private List<ResolveInfo> resolveComponent(ComponentName componentName,
401 UserHandle userHandle) {
mike dooley10a58312014-11-06 13:46:19 -0800402 PackageManager pm = mContext.getPackageManager();
403 Intent intent = new Intent(ConnectionService.SERVICE_INTERFACE);
404 intent.setComponent(componentName);
Evan Charlton105d9772014-11-25 14:08:53 -0800405 if (userHandle != null) {
406 return pm.queryIntentServicesAsUser(intent, 0, userHandle.getIdentifier());
407 } else {
408 return pm.queryIntentServices(intent, 0);
409 }
mike dooley10a58312014-11-06 13:46:19 -0800410 }
411
Tyler Gunn8e0fef42014-09-08 18:34:44 -0700412 /**
413 * Retrieves a list of all {@link PhoneAccountHandle}s registered.
414 *
415 * @return The list of {@link PhoneAccountHandle}s.
416 */
Ihab Awad293edf22014-07-24 17:52:29 -0700417 public List<PhoneAccountHandle> getAllPhoneAccountHandles() {
418 List<PhoneAccountHandle> accountHandles = new ArrayList<>();
419 for (PhoneAccount m : mState.accounts) {
Evan Charlton105d9772014-11-25 14:08:53 -0800420 if (isVisibleForUser(m)) {
421 accountHandles.add(m.getAccountHandle());
422 }
Ihab Awad293edf22014-07-24 17:52:29 -0700423 }
424 return accountHandles;
425 }
426
427 public List<PhoneAccount> getAllPhoneAccounts() {
Evan Charlton105d9772014-11-25 14:08:53 -0800428 List<PhoneAccount> accounts = new ArrayList<>(mState.accounts.size());
429 for (PhoneAccount account : mState.accounts) {
430 if (isVisibleForUser(account)) {
431 accounts.add(account);
432 }
433 }
434 return accounts;
Tyler Gunn8e0fef42014-09-08 18:34:44 -0700435 }
436
437 /**
Nancy Chen309198e2014-09-15 18:02:49 -0700438 * Retrieves a list of all call provider phone accounts.
Tyler Gunn8e0fef42014-09-08 18:34:44 -0700439 *
440 * @return The phone account handles.
441 */
Nancy Chen309198e2014-09-15 18:02:49 -0700442 public List<PhoneAccountHandle> getCallCapablePhoneAccounts() {
Santos Cordonafe59e52014-08-22 16:48:43 -0700443 return getPhoneAccountHandles(PhoneAccount.CAPABILITY_CALL_PROVIDER);
444 }
445
Tyler Gunn8e0fef42014-09-08 18:34:44 -0700446 /**
Nancy Chen309198e2014-09-15 18:02:49 -0700447 * Retrieves a list of all phone account call provider phone accounts supporting the
Tyler Gunn8e0fef42014-09-08 18:34:44 -0700448 * specified URI scheme.
449 *
450 * @param uriScheme The URI scheme.
451 * @return The phone account handles.
452 */
Nancy Chen309198e2014-09-15 18:02:49 -0700453 public List<PhoneAccountHandle> getCallCapablePhoneAccounts(String uriScheme) {
454 return getPhoneAccountHandles(PhoneAccount.CAPABILITY_CALL_PROVIDER, uriScheme);
Tyler Gunn84253572014-09-02 14:50:05 -0700455 }
456
Tyler Gunn8e0fef42014-09-08 18:34:44 -0700457 /**
Nancy Chen1c5926f2014-09-17 14:44:14 -0700458 * Retrieves a list of all phone accounts registered by a specified package.
459 *
460 * @param packageName The name of the package that registered the phone accounts.
461 * @return The phone account handles.
462 */
463 public List<PhoneAccountHandle> getPhoneAccountsForPackage(String packageName) {
464 List<PhoneAccountHandle> accountHandles = new ArrayList<>();
465 for (PhoneAccount m : mState.accounts) {
466 if (Objects.equals(
467 packageName,
Evan Charlton105d9772014-11-25 14:08:53 -0800468 m.getAccountHandle().getComponentName().getPackageName())
469 && isVisibleForUser(m)) {
Nancy Chen1c5926f2014-09-17 14:44:14 -0700470 accountHandles.add(m.getAccountHandle());
471 }
472 }
473 return accountHandles;
474 }
475
476 /**
Nancy Chen309198e2014-09-15 18:02:49 -0700477 * Retrieves a list of all phone account handles with the connection manager capability.
Tyler Gunn8e0fef42014-09-08 18:34:44 -0700478 *
479 * @return The phone account handles.
480 */
481 public List<PhoneAccountHandle> getConnectionManagerPhoneAccounts() {
Santos Cordone3d82452014-09-15 13:44:29 -0700482 return getPhoneAccountHandles(PhoneAccount.CAPABILITY_CONNECTION_MANAGER,
Nancy Chen309198e2014-09-15 18:02:49 -0700483 null /* supportedUriScheme */);
Santos Cordon176ae282014-07-14 02:02:14 -0700484 }
485
Ihab Awad293edf22014-07-24 17:52:29 -0700486 public PhoneAccount getPhoneAccount(PhoneAccountHandle handle) {
487 for (PhoneAccount m : mState.accounts) {
Evan Charlton105d9772014-11-25 14:08:53 -0800488 if (Objects.equals(handle, m.getAccountHandle())
489 && isVisibleForUser(m)) {
Ihab Awad104f8062014-07-17 11:29:35 -0700490 return m;
Santos Cordon176ae282014-07-14 02:02:14 -0700491 }
492 }
493 return null;
494 }
495
Ihab Awad104f8062014-07-17 11:29:35 -0700496 // TODO: Should we implement an artificial limit for # of accounts associated with a single
497 // ComponentName?
Ihab Awad293edf22014-07-24 17:52:29 -0700498 public void registerPhoneAccount(PhoneAccount account) {
Tyler Gunncb59b672014-08-20 09:02:11 -0700499 // Enforce the requirement that a connection service for a phone account has the correct
500 // permission.
501 if (!phoneAccountHasPermission(account.getAccountHandle())) {
502 Log.w(this, "Phone account %s does not have BIND_CONNECTION_SERVICE permission.",
503 account.getAccountHandle());
504 throw new SecurityException(
505 "PhoneAccount connection service requires BIND_CONNECTION_SERVICE permission.");
506 }
507
Tyler Gunn8e0fef42014-09-08 18:34:44 -0700508 addOrReplacePhoneAccount(account);
509 }
510
511 /**
512 * Adds a {@code PhoneAccount}, replacing an existing one if found.
513 *
514 * @param account The {@code PhoneAccount} to add or replace.
515 */
516 private void addOrReplacePhoneAccount(PhoneAccount account) {
Ihab Awad7e2c7f32014-11-03 09:49:45 -0800517 Log.d(this, "addOrReplacePhoneAccount(%s -> %s)",
518 account.getAccountHandle(), account);
519
Ihab Awad293edf22014-07-24 17:52:29 -0700520 mState.accounts.add(account);
Ihab Awad104f8062014-07-17 11:29:35 -0700521 // Search for duplicates and remove any that are found.
Ihab Awad293edf22014-07-24 17:52:29 -0700522 for (int i = 0; i < mState.accounts.size() - 1; i++) {
523 if (Objects.equals(
524 account.getAccountHandle(), mState.accounts.get(i).getAccountHandle())) {
Ihab Awad104f8062014-07-17 11:29:35 -0700525 // replace existing entry.
Ihab Awad293edf22014-07-24 17:52:29 -0700526 mState.accounts.remove(i);
Ihab Awad104f8062014-07-17 11:29:35 -0700527 break;
Santos Cordon176ae282014-07-14 02:02:14 -0700528 }
529 }
530
Ihab Awad293edf22014-07-24 17:52:29 -0700531 write();
Ihab Awadb78b2762014-07-25 15:16:23 -0700532 fireAccountsChanged();
Ihab Awad293edf22014-07-24 17:52:29 -0700533 }
534
Evan Charlton89176372014-07-19 18:23:09 -0700535 public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) {
Ihab Awad293edf22014-07-24 17:52:29 -0700536 for (int i = 0; i < mState.accounts.size(); i++) {
Evan Charlton105d9772014-11-25 14:08:53 -0800537 PhoneAccountHandle handle = mState.accounts.get(i).getAccountHandle();
538 if (Objects.equals(accountHandle, handle)) {
Ihab Awad293edf22014-07-24 17:52:29 -0700539 mState.accounts.remove(i);
Ihab Awad104f8062014-07-17 11:29:35 -0700540 break;
541 }
542 }
543
Ihab Awad293edf22014-07-24 17:52:29 -0700544 write();
Ihab Awadb78b2762014-07-25 15:16:23 -0700545 fireAccountsChanged();
Santos Cordon176ae282014-07-14 02:02:14 -0700546 }
547
Tyler Gunnd900ce62014-08-13 11:40:59 -0700548 /**
549 * Un-registers all phone accounts associated with a specified package.
550 *
551 * @param packageName The package for which phone accounts will be removed.
Evan Charlton105d9772014-11-25 14:08:53 -0800552 * @param userHandle The {@link UserHandle} the package is running under.
Tyler Gunnd900ce62014-08-13 11:40:59 -0700553 */
Evan Charlton105d9772014-11-25 14:08:53 -0800554 public void clearAccounts(String packageName, UserHandle userHandle) {
Tyler Gunnd900ce62014-08-13 11:40:59 -0700555 boolean accountsRemoved = false;
556 Iterator<PhoneAccount> it = mState.accounts.iterator();
557 while (it.hasNext()) {
558 PhoneAccount phoneAccount = it.next();
Evan Charlton105d9772014-11-25 14:08:53 -0800559 PhoneAccountHandle handle = phoneAccount.getAccountHandle();
560 if (Objects.equals(packageName, handle.getComponentName().getPackageName())
561 && Objects.equals(userHandle, handle.getUserHandle())) {
Tyler Gunnd900ce62014-08-13 11:40:59 -0700562 Log.i(this, "Removing phone account " + phoneAccount.getLabel());
563 it.remove();
564 accountsRemoved = true;
Ihab Awad104f8062014-07-17 11:29:35 -0700565 }
566 }
567
Tyler Gunnd900ce62014-08-13 11:40:59 -0700568 if (accountsRemoved) {
569 write();
570 fireAccountsChanged();
571 }
Ihab Awadb78b2762014-07-25 15:16:23 -0700572 }
573
Nancy Chen140004a2014-10-15 15:48:38 -0700574 public boolean isVoiceMailNumber(PhoneAccountHandle accountHandle, String number) {
Wink Saville35850602014-10-23 15:57:21 -0700575 int subId = getSubscriptionIdForPhoneAccount(accountHandle);
Nancy Chen140004a2014-10-15 15:48:38 -0700576 return PhoneNumberUtils.isVoiceMailNumber(subId, number);
577 }
578
Ihab Awadb78b2762014-07-25 15:16:23 -0700579 public void addListener(Listener l) {
580 mListeners.add(l);
581 }
582
583 public void removeListener(Listener l) {
Jay Shraunera82c8f72014-08-14 15:49:16 -0700584 if (l != null) {
585 mListeners.remove(l);
586 }
Ihab Awadb78b2762014-07-25 15:16:23 -0700587 }
588
589 private void fireAccountsChanged() {
590 for (Listener l : mListeners) {
591 l.onAccountsChanged(this);
592 }
593 }
594
595 private void fireDefaultOutgoingChanged() {
596 for (Listener l : mListeners) {
597 l.onDefaultOutgoingChanged(this);
598 }
599 }
600
601 private void fireSimCallManagerChanged() {
602 for (Listener l : mListeners) {
603 l.onSimCallManagerChanged(this);
604 }
Santos Cordon176ae282014-07-14 02:02:14 -0700605 }
606
Tyler Gunncb59b672014-08-20 09:02:11 -0700607 /**
608 * Determines if the connection service specified by a {@link PhoneAccountHandle} has the
609 * {@link Manifest.permission#BIND_CONNECTION_SERVICE} permission.
610 *
611 * @param phoneAccountHandle The phone account to check.
612 * @return {@code True} if the phone account has permission.
613 */
614 public boolean phoneAccountHasPermission(PhoneAccountHandle phoneAccountHandle) {
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700615 PackageManager packageManager = mContext.getPackageManager();
Tyler Gunncb59b672014-08-20 09:02:11 -0700616 try {
617 ServiceInfo serviceInfo = packageManager.getServiceInfo(
618 phoneAccountHandle.getComponentName(), 0);
619
620 return serviceInfo.permission != null &&
621 serviceInfo.permission.equals(Manifest.permission.BIND_CONNECTION_SERVICE);
622 } catch (PackageManager.NameNotFoundException e) {
623 Log.w(this, "Name not found %s", e);
624 return false;
625 }
626 }
627
Ihab Awad293edf22014-07-24 17:52:29 -0700628 ////////////////////////////////////////////////////////////////////////////////////////////////
629
Santos Cordonafe59e52014-08-22 16:48:43 -0700630 /**
631 * Returns a list of phone account handles with the specified flag.
Tyler Gunn84253572014-09-02 14:50:05 -0700632 *
633 * @param flags Flags which the {@code PhoneAccount} must have.
Santos Cordonafe59e52014-08-22 16:48:43 -0700634 */
635 private List<PhoneAccountHandle> getPhoneAccountHandles(int flags) {
Nancy Chen309198e2014-09-15 18:02:49 -0700636 return getPhoneAccountHandles(flags, null);
Tyler Gunn84253572014-09-02 14:50:05 -0700637 }
638
639 /**
640 * Returns a list of phone account handles with the specified flag, supporting the specified
Nancy Chen309198e2014-09-15 18:02:49 -0700641 * URI scheme.
Tyler Gunn84253572014-09-02 14:50:05 -0700642 *
643 * @param flags Flags which the {@code PhoneAccount} must have.
644 * @param uriScheme URI schemes the PhoneAccount must handle. {@code Null} bypasses the
645 * URI scheme check.
646 */
Nancy Chen309198e2014-09-15 18:02:49 -0700647 private List<PhoneAccountHandle> getPhoneAccountHandles(int flags, String uriScheme) {
Evan Charlton94d01622014-07-20 12:32:05 -0700648 List<PhoneAccountHandle> accountHandles = new ArrayList<>();
Ihab Awad293edf22014-07-24 17:52:29 -0700649 for (PhoneAccount m : mState.accounts) {
Evan Charlton105d9772014-11-25 14:08:53 -0800650 if (!m.hasCapabilities(flags)) {
651 // Account doesn't have the right capabilities; skip this one.
652 continue;
Ihab Awadf2a84912014-07-22 21:09:25 -0700653 }
Evan Charlton105d9772014-11-25 14:08:53 -0800654 if (uriScheme != null && !m.supportsUriScheme(uriScheme)) {
655 // Account doesn't support this URI scheme; skip this one.
656 continue;
657 }
658 if (resolveComponent(m.getAccountHandle()).isEmpty()) {
659 // This component cannot be resolved anymore; skip this one.
660 continue;
661 }
662 if (!isVisibleForUser(m)) {
663 // Account is not visible for the current user; skip this one.
664 continue;
665 }
666 accountHandles.add(m.getAccountHandle());
Ihab Awad104f8062014-07-17 11:29:35 -0700667 }
Evan Charlton94d01622014-07-20 12:32:05 -0700668 return accountHandles;
Ihab Awad104f8062014-07-17 11:29:35 -0700669 }
670
Ihab Awad293edf22014-07-24 17:52:29 -0700671 /**
672 * The state of this {@code PhoneAccountRegistrar}.
673 */
Ihab Awadb78b2762014-07-25 15:16:23 -0700674 @VisibleForTesting
675 public static class State {
Ihab Awad293edf22014-07-24 17:52:29 -0700676 /**
677 * The account selected by the user to be employed by default for making outgoing calls.
678 * If the user has not made such a selection, then this is null.
679 */
680 public PhoneAccountHandle defaultOutgoing = null;
681
682 /**
Ihab Awadb78b2762014-07-25 15:16:23 -0700683 * A {@code PhoneAccount} having {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} which
Ihab Awad293edf22014-07-24 17:52:29 -0700684 * manages and optimizes a user's PSTN SIM connections.
685 */
686 public PhoneAccountHandle simCallManager;
687
688 /**
Tyler Gunn7cc70b42014-09-12 22:17:27 -0700689 * The complete list of {@code PhoneAccount}s known to the Telecom subsystem.
Ihab Awad293edf22014-07-24 17:52:29 -0700690 */
691 public final List<PhoneAccount> accounts = new ArrayList<>();
Tyler Gunn84253572014-09-02 14:50:05 -0700692
693 /**
694 * The version number of the State data.
695 */
696 public int versionNumber;
Ihab Awad293edf22014-07-24 17:52:29 -0700697 }
698
Tyler Gunn9787e0e2014-10-14 14:36:12 -0700699 /**
700 * Dumps the state of the {@link CallsManager}.
701 *
702 * @param pw The {@code IndentingPrintWriter} to write the state to.
703 */
704 public void dump(IndentingPrintWriter pw) {
705 if (mState != null) {
706 pw.println("xmlVersion: " + mState.versionNumber);
707 pw.println("defaultOutgoing: " + (mState.defaultOutgoing == null ? "none" :
708 mState.defaultOutgoing));
709 pw.println("simCallManager: " + (mState.simCallManager == null ? "none" :
710 mState.simCallManager));
711 pw.println("phoneAccounts:");
712 pw.increaseIndent();
713 for (PhoneAccount phoneAccount : mState.accounts) {
714 pw.println(phoneAccount);
715 }
716 pw.decreaseIndent();
717 }
718 }
719
Ihab Awad293edf22014-07-24 17:52:29 -0700720 ////////////////////////////////////////////////////////////////////////////////////////////////
721 //
722 // State management
723 //
724
725 private void write() {
Ihab Awadb78b2762014-07-25 15:16:23 -0700726 final FileOutputStream os;
Ihab Awad104f8062014-07-17 11:29:35 -0700727 try {
Ihab Awadb78b2762014-07-25 15:16:23 -0700728 os = mAtomicFile.startWrite();
729 boolean success = false;
730 try {
731 XmlSerializer serializer = new FastXmlSerializer();
732 serializer.setOutput(new BufferedOutputStream(os), "utf-8");
Evan Charlton105d9772014-11-25 14:08:53 -0800733 writeToXml(mState, serializer, mContext);
Ihab Awadb78b2762014-07-25 15:16:23 -0700734 serializer.flush();
735 success = true;
736 } finally {
737 if (success) {
738 mAtomicFile.finishWrite(os);
739 } else {
740 mAtomicFile.failWrite(os);
741 }
742 }
743 } catch (IOException e) {
744 Log.e(this, e, "Writing state to XML file");
Ihab Awad104f8062014-07-17 11:29:35 -0700745 }
746 }
747
Ihab Awadb78b2762014-07-25 15:16:23 -0700748 private void read() {
749 final InputStream is;
Ihab Awad104f8062014-07-17 11:29:35 -0700750 try {
Ihab Awadb78b2762014-07-25 15:16:23 -0700751 is = mAtomicFile.openRead();
752 } catch (FileNotFoundException ex) {
753 return;
754 }
755
Tyler Gunn84253572014-09-02 14:50:05 -0700756 boolean versionChanged = false;
757
Ihab Awadb78b2762014-07-25 15:16:23 -0700758 XmlPullParser parser;
759 try {
760 parser = Xml.newPullParser();
761 parser.setInput(new BufferedInputStream(is), null);
762 parser.nextTag();
Tyler Gunn84253572014-09-02 14:50:05 -0700763 mState = readFromXml(parser, mContext);
764 versionChanged = mState.versionNumber < EXPECTED_STATE_VERSION;
765
Ihab Awadb78b2762014-07-25 15:16:23 -0700766 } catch (IOException | XmlPullParserException e) {
767 Log.e(this, e, "Reading state from XML file");
768 mState = new State();
769 } finally {
770 try {
771 is.close();
772 } catch (IOException e) {
773 Log.e(this, e, "Closing InputStream");
774 }
Ihab Awad104f8062014-07-17 11:29:35 -0700775 }
Tyler Gunn84253572014-09-02 14:50:05 -0700776
Evan Charlton105d9772014-11-25 14:08:53 -0800777 // Verify all of the UserHandles.
778 List<PhoneAccount> badAccounts = new ArrayList<>();
779 for (PhoneAccount phoneAccount : mState.accounts) {
780 UserHandle userHandle = phoneAccount.getAccountHandle().getUserHandle();
781 if (userHandle == null) {
782 Log.w(this, "Missing UserHandle for %s", phoneAccount);
783 badAccounts.add(phoneAccount);
784 } else if (mUserManager.getSerialNumberForUser(userHandle) == -1) {
785 Log.w(this, "User does not exist for %s", phoneAccount);
786 badAccounts.add(phoneAccount);
787 }
788 }
789 mState.accounts.removeAll(badAccounts);
790
Tyler Gunn84253572014-09-02 14:50:05 -0700791 // If an upgrade occurred, write out the changed data.
Evan Charlton105d9772014-11-25 14:08:53 -0800792 if (versionChanged || !badAccounts.isEmpty()) {
Tyler Gunn84253572014-09-02 14:50:05 -0700793 write();
794 }
Santos Cordon176ae282014-07-14 02:02:14 -0700795 }
796
Evan Charlton105d9772014-11-25 14:08:53 -0800797 private static void writeToXml(State state, XmlSerializer serializer, Context context)
Ihab Awadb78b2762014-07-25 15:16:23 -0700798 throws IOException {
Evan Charlton105d9772014-11-25 14:08:53 -0800799 sStateXml.writeToXml(state, serializer, context);
Santos Cordon176ae282014-07-14 02:02:14 -0700800 }
Ihab Awad104f8062014-07-17 11:29:35 -0700801
Tyler Gunn84253572014-09-02 14:50:05 -0700802 private static State readFromXml(XmlPullParser parser, Context context)
Ihab Awadb78b2762014-07-25 15:16:23 -0700803 throws IOException, XmlPullParserException {
Tyler Gunn84253572014-09-02 14:50:05 -0700804 State s = sStateXml.readFromXml(parser, 0, context);
Ihab Awadb78b2762014-07-25 15:16:23 -0700805 return s != null ? s : new State();
Ihab Awad104f8062014-07-17 11:29:35 -0700806 }
807
Ihab Awad293edf22014-07-24 17:52:29 -0700808 ////////////////////////////////////////////////////////////////////////////////////////////////
Ihab Awad104f8062014-07-17 11:29:35 -0700809 //
Ihab Awadb78b2762014-07-25 15:16:23 -0700810 // XML serialization
Ihab Awad104f8062014-07-17 11:29:35 -0700811 //
812
Ihab Awadb78b2762014-07-25 15:16:23 -0700813 @VisibleForTesting
Ihab Awad26923222014-07-30 10:54:35 -0700814 public abstract static class XmlSerialization<T> {
Tyler Gunn84253572014-09-02 14:50:05 -0700815 private static final String LENGTH_ATTRIBUTE = "length";
816 private static final String VALUE_TAG = "value";
817
Ihab Awadb78b2762014-07-25 15:16:23 -0700818 /**
819 * Write the supplied object to XML
820 */
Evan Charlton105d9772014-11-25 14:08:53 -0800821 public abstract void writeToXml(T o, XmlSerializer serializer, Context context)
Ihab Awad26923222014-07-30 10:54:35 -0700822 throws IOException;
Ihab Awadb78b2762014-07-25 15:16:23 -0700823
824 /**
825 * Read from the supplied XML into a new object, returning null in case of an
826 * unrecoverable schema mismatch or other data error. 'parser' must be already
827 * positioned at the first tag that is expected to have been emitted by this
828 * object's writeToXml(). This object tries to fail early without modifying
829 * 'parser' if it does not recognize the data it sees.
830 */
Tyler Gunn84253572014-09-02 14:50:05 -0700831 public abstract T readFromXml(XmlPullParser parser, int version, Context context)
Ihab Awad26923222014-07-30 10:54:35 -0700832 throws IOException, XmlPullParserException;
833
Ihab Awadd9f54382014-10-24 11:44:47 -0700834 protected void writeTextIfNonNull(String tagName, Object value, XmlSerializer serializer)
Ihab Awad26923222014-07-30 10:54:35 -0700835 throws IOException {
836 if (value != null) {
837 serializer.startTag(null, tagName);
838 serializer.text(Objects.toString(value));
839 serializer.endTag(null, tagName);
840 }
841 }
Tyler Gunn84253572014-09-02 14:50:05 -0700842
843 /**
844 * Serializes a string array.
845 *
846 * @param tagName The tag name for the string array.
847 * @param values The string values to serialize.
848 * @param serializer The serializer.
849 * @throws IOException
850 */
851 protected void writeStringList(String tagName, List<String> values,
852 XmlSerializer serializer)
853 throws IOException {
854
855 serializer.startTag(null, tagName);
856 if (values != null) {
857 serializer.attribute(null, LENGTH_ATTRIBUTE, Objects.toString(values.size()));
858 for (String toSerialize : values) {
859 serializer.startTag(null, VALUE_TAG);
860 if (toSerialize != null ){
861 serializer.text(toSerialize);
862 }
Tyler Gunn84253572014-09-02 14:50:05 -0700863 serializer.endTag(null, VALUE_TAG);
864 }
865 } else {
866 serializer.attribute(null, LENGTH_ATTRIBUTE, "0");
867 }
868 serializer.endTag(null, tagName);
Ihab Awadd9f54382014-10-24 11:44:47 -0700869 }
Tyler Gunn84253572014-09-02 14:50:05 -0700870
Ihab Awadd9f54382014-10-24 11:44:47 -0700871 protected void writeBitmapIfNonNull(String tagName, Bitmap value, XmlSerializer serializer)
872 throws IOException {
873 if (value != null && value.getByteCount() > 0) {
874 ByteArrayOutputStream stream = new ByteArrayOutputStream();
875 value.compress(Bitmap.CompressFormat.PNG, 100, stream);
876 byte[] imageByteArray = stream.toByteArray();
877 String text = Base64.encodeToString(imageByteArray, 0, imageByteArray.length, 0);
878
879 serializer.startTag(null, tagName);
880 serializer.text(text);
881 serializer.endTag(null, tagName);
882 }
Tyler Gunn84253572014-09-02 14:50:05 -0700883 }
884
Evan Charlton105d9772014-11-25 14:08:53 -0800885 protected void writeLong(String tagName, long value, XmlSerializer serializer)
886 throws IOException {
887 serializer.startTag(null, tagName);
888 serializer.text(Long.valueOf(value).toString());
889 serializer.endTag(null, tagName);
890 }
891
Tyler Gunn84253572014-09-02 14:50:05 -0700892 /**
893 * Reads a string array from the XML parser.
894 *
895 * @param parser The XML parser.
896 * @return String array containing the parsed values.
897 * @throws IOException Exception related to IO.
898 * @throws XmlPullParserException Exception related to parsing.
899 */
900 protected List<String> readStringList(XmlPullParser parser)
901 throws IOException, XmlPullParserException {
902
903 int length = Integer.parseInt(parser.getAttributeValue(null, LENGTH_ATTRIBUTE));
904 List<String> arrayEntries = new ArrayList<String>(length);
905 String value = null;
906
907 if (length == 0) {
908 return arrayEntries;
909 }
910
911 int outerDepth = parser.getDepth();
912 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
913 if (parser.getName().equals(VALUE_TAG)) {
Tyler Gunn8e0fef42014-09-08 18:34:44 -0700914 parser.next();
Tyler Gunn84253572014-09-02 14:50:05 -0700915 value = parser.getText();
916 arrayEntries.add(value);
917 }
918 }
919
920 return arrayEntries;
921 }
Ihab Awadd9f54382014-10-24 11:44:47 -0700922
923 protected Bitmap readBitmap(XmlPullParser parser)
924 throws IOException, XmlPullParserException {
925 byte[] imageByteArray = Base64.decode(parser.getText(), 0);
926 return BitmapFactory.decodeByteArray(imageByteArray, 0, imageByteArray.length);
927 }
Ihab Awad104f8062014-07-17 11:29:35 -0700928 }
929
Ihab Awadb78b2762014-07-25 15:16:23 -0700930 @VisibleForTesting
931 public static final XmlSerialization<State> sStateXml =
932 new XmlSerialization<State>() {
933 private static final String CLASS_STATE = "phone_account_registrar_state";
Ihab Awad104f8062014-07-17 11:29:35 -0700934 private static final String DEFAULT_OUTGOING = "default_outgoing";
Ihab Awad293edf22014-07-24 17:52:29 -0700935 private static final String SIM_CALL_MANAGER = "sim_call_manager";
Ihab Awad104f8062014-07-17 11:29:35 -0700936 private static final String ACCOUNTS = "accounts";
Tyler Gunn84253572014-09-02 14:50:05 -0700937 private static final String VERSION = "version";
Ihab Awad104f8062014-07-17 11:29:35 -0700938
939 @Override
Evan Charlton105d9772014-11-25 14:08:53 -0800940 public void writeToXml(State o, XmlSerializer serializer, Context context)
Ihab Awadb78b2762014-07-25 15:16:23 -0700941 throws IOException {
Ihab Awad26923222014-07-30 10:54:35 -0700942 if (o != null) {
943 serializer.startTag(null, CLASS_STATE);
Tyler Gunn84253572014-09-02 14:50:05 -0700944 serializer.attribute(null, VERSION, Objects.toString(EXPECTED_STATE_VERSION));
Ihab Awadb78b2762014-07-25 15:16:23 -0700945
Ihab Awad26923222014-07-30 10:54:35 -0700946 if (o.defaultOutgoing != null) {
947 serializer.startTag(null, DEFAULT_OUTGOING);
Evan Charlton105d9772014-11-25 14:08:53 -0800948 sPhoneAccountHandleXml.writeToXml(o.defaultOutgoing, serializer, context);
Ihab Awad26923222014-07-30 10:54:35 -0700949 serializer.endTag(null, DEFAULT_OUTGOING);
950 }
951
952 if (o.simCallManager != null) {
953 serializer.startTag(null, SIM_CALL_MANAGER);
Evan Charlton105d9772014-11-25 14:08:53 -0800954 sPhoneAccountHandleXml.writeToXml(o.simCallManager, serializer, context);
Ihab Awad26923222014-07-30 10:54:35 -0700955 serializer.endTag(null, SIM_CALL_MANAGER);
956 }
957
958 serializer.startTag(null, ACCOUNTS);
959 for (PhoneAccount m : o.accounts) {
Evan Charlton105d9772014-11-25 14:08:53 -0800960 sPhoneAccountXml.writeToXml(m, serializer, context);
Ihab Awad26923222014-07-30 10:54:35 -0700961 }
962 serializer.endTag(null, ACCOUNTS);
963
964 serializer.endTag(null, CLASS_STATE);
Ihab Awad293edf22014-07-24 17:52:29 -0700965 }
Ihab Awad104f8062014-07-17 11:29:35 -0700966 }
967
968 @Override
Tyler Gunn84253572014-09-02 14:50:05 -0700969 public State readFromXml(XmlPullParser parser, int version, Context context)
Ihab Awadb78b2762014-07-25 15:16:23 -0700970 throws IOException, XmlPullParserException {
971 if (parser.getName().equals(CLASS_STATE)) {
972 State s = new State();
Tyler Gunn84253572014-09-02 14:50:05 -0700973
974 String rawVersion = parser.getAttributeValue(null, VERSION);
975 s.versionNumber = TextUtils.isEmpty(rawVersion) ? 1 :
976 Integer.parseInt(rawVersion);
977
Ihab Awadb78b2762014-07-25 15:16:23 -0700978 int outerDepth = parser.getDepth();
979 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
980 if (parser.getName().equals(DEFAULT_OUTGOING)) {
981 parser.nextTag();
Tyler Gunn84253572014-09-02 14:50:05 -0700982 s.defaultOutgoing = sPhoneAccountHandleXml.readFromXml(parser,
983 s.versionNumber, context);
Ihab Awadb78b2762014-07-25 15:16:23 -0700984 } else if (parser.getName().equals(SIM_CALL_MANAGER)) {
985 parser.nextTag();
Tyler Gunn84253572014-09-02 14:50:05 -0700986 s.simCallManager = sPhoneAccountHandleXml.readFromXml(parser,
987 s.versionNumber, context);
Evan Charlton105d9772014-11-25 14:08:53 -0800988 if (s.simCallManager.getUserHandle() == null) {
989 // This should never happen, but handle the upgrade case.
990 s.simCallManager = new PhoneAccountHandle(
991 s.simCallManager.getComponentName(),
992 s.simCallManager.getId(),
993 Process.myUserHandle());
994 }
Ihab Awadb78b2762014-07-25 15:16:23 -0700995 } else if (parser.getName().equals(ACCOUNTS)) {
996 int accountsDepth = parser.getDepth();
997 while (XmlUtils.nextElementWithin(parser, accountsDepth)) {
Tyler Gunn84253572014-09-02 14:50:05 -0700998 PhoneAccount account = sPhoneAccountXml.readFromXml(parser,
999 s.versionNumber, context);
1000
1001 if (account != null && s.accounts != null) {
Ihab Awadb78b2762014-07-25 15:16:23 -07001002 s.accounts.add(account);
1003 }
1004 }
Ihab Awad104f8062014-07-17 11:29:35 -07001005 }
1006 }
Ihab Awadb78b2762014-07-25 15:16:23 -07001007 return s;
Ihab Awad104f8062014-07-17 11:29:35 -07001008 }
Ihab Awadb78b2762014-07-25 15:16:23 -07001009 return null;
Ihab Awad104f8062014-07-17 11:29:35 -07001010 }
1011 };
1012
Ihab Awadb78b2762014-07-25 15:16:23 -07001013 @VisibleForTesting
1014 public static final XmlSerialization<PhoneAccount> sPhoneAccountXml =
1015 new XmlSerialization<PhoneAccount>() {
1016 private static final String CLASS_PHONE_ACCOUNT = "phone_account";
1017 private static final String ACCOUNT_HANDLE = "account_handle";
Andrew Lee7129f1c2014-09-04 11:55:07 -07001018 private static final String ADDRESS = "handle";
1019 private static final String SUBSCRIPTION_ADDRESS = "subscription_number";
Ihab Awad104f8062014-07-17 11:29:35 -07001020 private static final String CAPABILITIES = "capabilities";
1021 private static final String ICON_RES_ID = "icon_res_id";
Ihab Awadd9f54382014-10-24 11:44:47 -07001022 private static final String ICON_PACKAGE_NAME = "icon_package_name";
1023 private static final String ICON_BITMAP = "icon_bitmap";
Ihab Awad7e2c7f32014-11-03 09:49:45 -08001024 private static final String ICON_TINT = "icon_tint";
1025 private static final String HIGHLIGHT_COLOR = "highlight_color";
Ihab Awad104f8062014-07-17 11:29:35 -07001026 private static final String LABEL = "label";
1027 private static final String SHORT_DESCRIPTION = "short_description";
Tyler Gunn84253572014-09-02 14:50:05 -07001028 private static final String SUPPORTED_URI_SCHEMES = "supported_uri_schemes";
Ihab Awad104f8062014-07-17 11:29:35 -07001029
1030 @Override
Evan Charlton105d9772014-11-25 14:08:53 -08001031 public void writeToXml(PhoneAccount o, XmlSerializer serializer, Context context)
Ihab Awadb78b2762014-07-25 15:16:23 -07001032 throws IOException {
Ihab Awad26923222014-07-30 10:54:35 -07001033 if (o != null) {
1034 serializer.startTag(null, CLASS_PHONE_ACCOUNT);
Ihab Awadb78b2762014-07-25 15:16:23 -07001035
Ihab Awad26923222014-07-30 10:54:35 -07001036 if (o.getAccountHandle() != null) {
1037 serializer.startTag(null, ACCOUNT_HANDLE);
Evan Charlton105d9772014-11-25 14:08:53 -08001038 sPhoneAccountHandleXml.writeToXml(o.getAccountHandle(), serializer, context);
Ihab Awad26923222014-07-30 10:54:35 -07001039 serializer.endTag(null, ACCOUNT_HANDLE);
1040 }
Ihab Awadb78b2762014-07-25 15:16:23 -07001041
Ihab Awadd9f54382014-10-24 11:44:47 -07001042 writeTextIfNonNull(ADDRESS, o.getAddress(), serializer);
1043 writeTextIfNonNull(SUBSCRIPTION_ADDRESS, o.getSubscriptionAddress(), serializer);
1044 writeTextIfNonNull(CAPABILITIES, Integer.toString(o.getCapabilities()), serializer);
1045 writeTextIfNonNull(ICON_RES_ID, Integer.toString(o.getIconResId()), serializer);
1046 writeTextIfNonNull(ICON_PACKAGE_NAME, o.getIconPackageName(), serializer);
1047 writeBitmapIfNonNull(ICON_BITMAP, o.getIconBitmap(), serializer);
Ihab Awad7e2c7f32014-11-03 09:49:45 -08001048 writeTextIfNonNull(ICON_TINT, Integer.toString(o.getIconTint()), serializer);
1049 writeTextIfNonNull(HIGHLIGHT_COLOR,
1050 Integer.toString(o.getHighlightColor()), serializer);
Ihab Awadd9f54382014-10-24 11:44:47 -07001051 writeTextIfNonNull(LABEL, o.getLabel(), serializer);
1052 writeTextIfNonNull(SHORT_DESCRIPTION, o.getShortDescription(), serializer);
Tyler Gunn84253572014-09-02 14:50:05 -07001053 writeStringList(SUPPORTED_URI_SCHEMES, o.getSupportedUriSchemes(), serializer);
Ihab Awadb78b2762014-07-25 15:16:23 -07001054
Ihab Awad26923222014-07-30 10:54:35 -07001055 serializer.endTag(null, CLASS_PHONE_ACCOUNT);
1056 }
Ihab Awad104f8062014-07-17 11:29:35 -07001057 }
1058
Tyler Gunn84253572014-09-02 14:50:05 -07001059 public PhoneAccount readFromXml(XmlPullParser parser, int version, Context context)
Ihab Awadb78b2762014-07-25 15:16:23 -07001060 throws IOException, XmlPullParserException {
1061 if (parser.getName().equals(CLASS_PHONE_ACCOUNT)) {
1062 int outerDepth = parser.getDepth();
1063 PhoneAccountHandle accountHandle = null;
Andrew Lee7129f1c2014-09-04 11:55:07 -07001064 Uri address = null;
1065 Uri subscriptionAddress = null;
Ihab Awadb78b2762014-07-25 15:16:23 -07001066 int capabilities = 0;
Ihab Awad7e2c7f32014-11-03 09:49:45 -08001067 int iconResId = PhoneAccount.NO_RESOURCE_ID;
Ihab Awadd9f54382014-10-24 11:44:47 -07001068 String iconPackageName = null;
Ihab Awad7e2c7f32014-11-03 09:49:45 -08001069 Bitmap iconBitmap = null;
Ihab Awad07bc5ee2014-11-12 13:42:52 -08001070 int iconTint = PhoneAccount.NO_ICON_TINT;
1071 int highlightColor = PhoneAccount.NO_HIGHLIGHT_COLOR;
Ihab Awadb78b2762014-07-25 15:16:23 -07001072 String label = null;
1073 String shortDescription = null;
Tyler Gunn84253572014-09-02 14:50:05 -07001074 List<String> supportedUriSchemes = null;
Ihab Awadb78b2762014-07-25 15:16:23 -07001075
1076 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
1077 if (parser.getName().equals(ACCOUNT_HANDLE)) {
1078 parser.nextTag();
Tyler Gunn84253572014-09-02 14:50:05 -07001079 accountHandle = sPhoneAccountHandleXml.readFromXml(parser, version,
1080 context);
Andrew Lee7129f1c2014-09-04 11:55:07 -07001081 } else if (parser.getName().equals(ADDRESS)) {
Ihab Awadb78b2762014-07-25 15:16:23 -07001082 parser.next();
Andrew Lee7129f1c2014-09-04 11:55:07 -07001083 address = Uri.parse(parser.getText());
1084 } else if (parser.getName().equals(SUBSCRIPTION_ADDRESS)) {
Ihab Awadb78b2762014-07-25 15:16:23 -07001085 parser.next();
Andrew Lee7129f1c2014-09-04 11:55:07 -07001086 String nextText = parser.getText();
1087 subscriptionAddress = nextText == null ? null : Uri.parse(nextText);
Ihab Awadb78b2762014-07-25 15:16:23 -07001088 } else if (parser.getName().equals(CAPABILITIES)) {
1089 parser.next();
1090 capabilities = Integer.parseInt(parser.getText());
1091 } else if (parser.getName().equals(ICON_RES_ID)) {
1092 parser.next();
1093 iconResId = Integer.parseInt(parser.getText());
Ihab Awadd9f54382014-10-24 11:44:47 -07001094 } else if (parser.getName().equals(ICON_PACKAGE_NAME)) {
1095 parser.next();
1096 iconPackageName = parser.getText();
1097 } else if (parser.getName().equals(ICON_BITMAP)) {
1098 parser.next();
Ihab Awad7e2c7f32014-11-03 09:49:45 -08001099 iconBitmap = readBitmap(parser);
1100 } else if (parser.getName().equals(ICON_TINT)) {
Nancy Chen06ce0622014-10-23 01:17:35 +00001101 parser.next();
Ihab Awad7e2c7f32014-11-03 09:49:45 -08001102 iconTint = Integer.parseInt(parser.getText());
1103 } else if (parser.getName().equals(HIGHLIGHT_COLOR)) {
1104 parser.next();
1105 highlightColor = Integer.parseInt(parser.getText());
Ihab Awadb78b2762014-07-25 15:16:23 -07001106 } else if (parser.getName().equals(LABEL)) {
1107 parser.next();
1108 label = parser.getText();
1109 } else if (parser.getName().equals(SHORT_DESCRIPTION)) {
1110 parser.next();
1111 shortDescription = parser.getText();
Tyler Gunn84253572014-09-02 14:50:05 -07001112 } else if (parser.getName().equals(SUPPORTED_URI_SCHEMES)) {
1113 supportedUriSchemes = readStringList(parser);
Ihab Awadb78b2762014-07-25 15:16:23 -07001114 }
1115 }
Tyler Gunn84253572014-09-02 14:50:05 -07001116
1117 // Upgrade older phone accounts to specify the supported URI schemes.
1118 if (version < 2) {
1119 ComponentName sipComponentName = new ComponentName("com.android.phone",
1120 "com.android.services.telephony.sip.SipConnectionService");
1121
1122 supportedUriSchemes = new ArrayList<>();
1123
1124 // Handle the SIP connection service.
1125 // Check the system settings to see if it also should handle "tel" calls.
1126 if (accountHandle.getComponentName().equals(sipComponentName)) {
1127 boolean useSipForPstn = useSipForPstnCalls(context);
1128 supportedUriSchemes.add(PhoneAccount.SCHEME_SIP);
1129 if (useSipForPstn) {
1130 supportedUriSchemes.add(PhoneAccount.SCHEME_TEL);
1131 }
1132 } else {
1133 supportedUriSchemes.add(PhoneAccount.SCHEME_TEL);
1134 supportedUriSchemes.add(PhoneAccount.SCHEME_VOICEMAIL);
1135 }
1136 }
1137
Ihab Awad7e2c7f32014-11-03 09:49:45 -08001138 // Upgrade older phone accounts with explicit package name
1139 if (version < 5) {
1140 if (iconBitmap == null) {
1141 iconPackageName = accountHandle.getComponentName().getPackageName();
1142 }
1143 }
1144
1145 PhoneAccount.Builder builder = PhoneAccount.builder(accountHandle, label)
Andrew Lee7129f1c2014-09-04 11:55:07 -07001146 .setAddress(address)
1147 .setSubscriptionAddress(subscriptionAddress)
1148 .setCapabilities(capabilities)
Andrew Lee7129f1c2014-09-04 11:55:07 -07001149 .setShortDescription(shortDescription)
1150 .setSupportedUriSchemes(supportedUriSchemes)
Ihab Awad7e2c7f32014-11-03 09:49:45 -08001151 .setHighlightColor(highlightColor);
1152
1153 if (iconBitmap == null) {
1154 builder.setIcon(iconPackageName, iconResId, iconTint);
1155 } else {
1156 builder.setIcon(iconBitmap);
1157 }
1158
1159 return builder.build();
Ihab Awadb78b2762014-07-25 15:16:23 -07001160 }
1161 return null;
Ihab Awad104f8062014-07-17 11:29:35 -07001162 }
Tyler Gunn84253572014-09-02 14:50:05 -07001163
1164 /**
1165 * Determines if the SIP call settings specify to use SIP for all calls, including PSTN calls.
1166 *
1167 * @param context The context.
1168 * @return {@code True} if SIP should be used for all calls.
1169 */
1170 private boolean useSipForPstnCalls(Context context) {
1171 String option = Settings.System.getString(context.getContentResolver(),
1172 Settings.System.SIP_CALL_OPTIONS);
1173 option = (option != null) ? option : Settings.System.SIP_ADDRESS_ONLY;
1174 return option.equals(Settings.System.SIP_ALWAYS);
1175 }
Ihab Awad104f8062014-07-17 11:29:35 -07001176 };
1177
Ihab Awadb78b2762014-07-25 15:16:23 -07001178 @VisibleForTesting
1179 public static final XmlSerialization<PhoneAccountHandle> sPhoneAccountHandleXml =
1180 new XmlSerialization<PhoneAccountHandle>() {
1181 private static final String CLASS_PHONE_ACCOUNT_HANDLE = "phone_account_handle";
Ihab Awad104f8062014-07-17 11:29:35 -07001182 private static final String COMPONENT_NAME = "component_name";
1183 private static final String ID = "id";
Evan Charlton105d9772014-11-25 14:08:53 -08001184 private static final String USER_SERIAL_NUMBER = "user_serial_number";
Ihab Awad104f8062014-07-17 11:29:35 -07001185
1186 @Override
Evan Charlton105d9772014-11-25 14:08:53 -08001187 public void writeToXml(PhoneAccountHandle o, XmlSerializer serializer, Context context)
Ihab Awadb78b2762014-07-25 15:16:23 -07001188 throws IOException {
Ihab Awad26923222014-07-30 10:54:35 -07001189 if (o != null) {
1190 serializer.startTag(null, CLASS_PHONE_ACCOUNT_HANDLE);
Ihab Awadb78b2762014-07-25 15:16:23 -07001191
Ihab Awad26923222014-07-30 10:54:35 -07001192 if (o.getComponentName() != null) {
Ihab Awadd9f54382014-10-24 11:44:47 -07001193 writeTextIfNonNull(
Ihab Awad26923222014-07-30 10:54:35 -07001194 COMPONENT_NAME, o.getComponentName().flattenToString(), serializer);
1195 }
Ihab Awadb78b2762014-07-25 15:16:23 -07001196
Ihab Awadd9f54382014-10-24 11:44:47 -07001197 writeTextIfNonNull(ID, o.getId(), serializer);
Ihab Awadb78b2762014-07-25 15:16:23 -07001198
Evan Charlton105d9772014-11-25 14:08:53 -08001199 if (o.getUserHandle() != null && context != null) {
1200 UserManager userManager = UserManager.get(context);
1201 writeLong(USER_SERIAL_NUMBER,
1202 userManager.getSerialNumberForUser(o.getUserHandle()), serializer);
1203 }
1204
Ihab Awad26923222014-07-30 10:54:35 -07001205 serializer.endTag(null, CLASS_PHONE_ACCOUNT_HANDLE);
1206 }
Ihab Awad104f8062014-07-17 11:29:35 -07001207 }
1208
1209 @Override
Tyler Gunn84253572014-09-02 14:50:05 -07001210 public PhoneAccountHandle readFromXml(XmlPullParser parser, int version, Context context)
Ihab Awadb78b2762014-07-25 15:16:23 -07001211 throws IOException, XmlPullParserException {
1212 if (parser.getName().equals(CLASS_PHONE_ACCOUNT_HANDLE)) {
1213 String componentNameString = null;
1214 String idString = null;
Evan Charlton105d9772014-11-25 14:08:53 -08001215 String userSerialNumberString = null;
Ihab Awadb78b2762014-07-25 15:16:23 -07001216 int outerDepth = parser.getDepth();
Evan Charlton105d9772014-11-25 14:08:53 -08001217
1218 UserManager userManager = UserManager.get(context);
1219
Ihab Awadb78b2762014-07-25 15:16:23 -07001220 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
1221 if (parser.getName().equals(COMPONENT_NAME)) {
1222 parser.next();
1223 componentNameString = parser.getText();
1224 } else if (parser.getName().equals(ID)) {
1225 parser.next();
1226 idString = parser.getText();
Evan Charlton105d9772014-11-25 14:08:53 -08001227 } else if (parser.getName().equals(USER_SERIAL_NUMBER)) {
1228 parser.next();
1229 userSerialNumberString = parser.getText();
Ihab Awadb78b2762014-07-25 15:16:23 -07001230 }
1231 }
Ihab Awad26923222014-07-30 10:54:35 -07001232 if (componentNameString != null) {
Evan Charlton105d9772014-11-25 14:08:53 -08001233 UserHandle userHandle = null;
1234 if (userSerialNumberString != null) {
1235 try {
1236 long serialNumber = Long.parseLong(userSerialNumberString);
1237 userHandle = userManager.getUserForSerialNumber(serialNumber);
1238 } catch (NumberFormatException e) {
1239 Log.e(this, e, "Could not parse UserHandle " + userSerialNumberString);
1240 }
1241 }
Ihab Awadb78b2762014-07-25 15:16:23 -07001242 return new PhoneAccountHandle(
1243 ComponentName.unflattenFromString(componentNameString),
Evan Charlton105d9772014-11-25 14:08:53 -08001244 idString,
1245 userHandle);
Ihab Awadb78b2762014-07-25 15:16:23 -07001246 }
1247 }
1248 return null;
Ihab Awad104f8062014-07-17 11:29:35 -07001249 }
1250 };
Santos Cordon176ae282014-07-14 02:02:14 -07001251}