blob: 65847b8eab2b450290d167c4bf492322fe692e12 [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
Sailesh Nepalfc43ea82015-07-28 19:30:28 -070019import android.app.ActivityManager;
Tyler Gunncb59b672014-08-20 09:02:11 -070020import android.Manifest;
Evan Charlton105d9772014-11-25 14:08:53 -080021import android.content.ComponentName;
22import android.content.Context;
Evan Charltonaf51ceb2014-07-30 11:56:36 -070023import android.content.Intent;
24import android.content.pm.PackageManager;
25import android.content.pm.ResolveInfo;
Tyler Gunncb59b672014-08-20 09:02:11 -070026import android.content.pm.ServiceInfo;
Evan Charlton105d9772014-11-25 14:08:53 -080027import android.content.pm.UserInfo;
Ihab Awadd9f54382014-10-24 11:44:47 -070028import android.graphics.Bitmap;
29import android.graphics.BitmapFactory;
Santos Cordon9c30c282015-05-13 16:28:27 -070030import android.graphics.drawable.Icon;
Evan Charlton105d9772014-11-25 14:08:53 -080031import android.net.Uri;
Sailesh Nepal91fc8092015-02-14 15:44:55 -080032import android.os.Binder;
Sailesh Nepalaecfa9b2015-06-16 14:55:27 -070033import android.os.PersistableBundle;
Evan Charlton105d9772014-11-25 14:08:53 -080034import android.os.Process;
35import android.os.UserHandle;
36import android.os.UserManager;
Tyler Gunn84253572014-09-02 14:50:05 -070037import android.provider.Settings;
Tyler Gunn7cc70b42014-09-12 22:17:27 -070038import android.telecom.ConnectionService;
Santos Cordon59c21a72015-06-11 10:11:21 -070039import android.telecom.DefaultDialerManager;
Tyler Gunn7cc70b42014-09-12 22:17:27 -070040import android.telecom.PhoneAccount;
41import android.telecom.PhoneAccountHandle;
Sailesh Nepalaecfa9b2015-06-16 14:55:27 -070042import android.telephony.CarrierConfigManager;
Nancy Chen140004a2014-10-15 15:48:38 -070043import android.telephony.PhoneNumberUtils;
Nancy Chen5a36b6e2014-10-23 17:42:42 -070044import android.telephony.SubscriptionManager;
Santos Cordon479b3022015-02-06 04:27:15 -080045import android.telephony.TelephonyManager;
Evan Charltonaf51ceb2014-07-30 11:56:36 -070046import android.text.TextUtils;
Ihab Awadb78b2762014-07-25 15:16:23 -070047import android.util.AtomicFile;
Ihab Awadd9f54382014-10-24 11:44:47 -070048import android.util.Base64;
Ihab Awadb78b2762014-07-25 15:16:23 -070049import android.util.Xml;
Santos Cordon176ae282014-07-14 02:02:14 -070050
Tyler Gunn91d43cf2014-09-17 12:19:39 -070051// TODO: Needed for move to system service: import com.android.internal.R;
Sailesh Nepal0e1dc5a2014-07-30 11:08:54 -070052import com.android.internal.annotations.VisibleForTesting;
53import com.android.internal.util.FastXmlSerializer;
Tyler Gunn9787e0e2014-10-14 14:36:12 -070054import com.android.internal.util.IndentingPrintWriter;
Sailesh Nepal0e1dc5a2014-07-30 11:08:54 -070055import com.android.internal.util.XmlUtils;
56
Evan Charltonaf51ceb2014-07-30 11:56:36 -070057import org.xmlpull.v1.XmlPullParser;
58import org.xmlpull.v1.XmlPullParserException;
59import org.xmlpull.v1.XmlSerializer;
60
Ihab Awadb78b2762014-07-25 15:16:23 -070061import java.io.BufferedInputStream;
62import java.io.BufferedOutputStream;
Santos Cordon9c30c282015-05-13 16:28:27 -070063import java.io.ByteArrayInputStream;
Ihab Awadd9f54382014-10-24 11:44:47 -070064import java.io.ByteArrayOutputStream;
Ihab Awadb78b2762014-07-25 15:16:23 -070065import java.io.File;
66import java.io.FileNotFoundException;
67import java.io.FileOutputStream;
68import java.io.IOException;
69import java.io.InputStream;
Tyler Gunn84253572014-09-02 14:50:05 -070070import java.lang.Integer;
Tyler Gunncb59b672014-08-20 09:02:11 -070071import java.lang.SecurityException;
Tyler Gunnd900ce62014-08-13 11:40:59 -070072import java.lang.String;
Santos Cordon176ae282014-07-14 02:02:14 -070073import java.util.ArrayList;
Sailesh Nepal91fc8092015-02-14 15:44:55 -080074import java.util.Collections;
Tyler Gunnd900ce62014-08-13 11:40:59 -070075import java.util.Iterator;
Santos Cordon176ae282014-07-14 02:02:14 -070076import java.util.List;
77import java.util.Objects;
Ihab Awadb78b2762014-07-25 15:16:23 -070078import java.util.concurrent.CopyOnWriteArrayList;
Santos Cordon176ae282014-07-14 02:02:14 -070079
80/**
Evan Charlton89176372014-07-19 18:23:09 -070081 * Handles writing and reading PhoneAccountHandle registration entries. This is a simple verbatim
Santos Cordon6a212642015-05-08 16:35:23 -070082 * delegate for all the account handling methods on {@link android.telecom.TelecomManager} as
83 * implemented in {@link TelecomServiceImpl}, with the notable exception that
84 * {@link TelecomServiceImpl} is responsible for security checking to make sure that the caller has
85 * proper authority over the {@code ComponentName}s they are declaring in their
86 * {@code PhoneAccountHandle}s.
87 *
88 *
89 * -- About Users and Phone Accounts --
90 *
Santos Cordon9c30c282015-05-13 16:28:27 -070091 * We store all phone accounts for all users in a single place, which means that there are three
92 * users that we have to deal with in code:
Santos Cordon6a212642015-05-08 16:35:23 -070093 * 1) The Android User that is currently active on the device.
94 * 2) The user which owns/registers the phone account.
95 * 3) The user running the app that is requesting the phone account information.
96 *
97 * For example, I have a device with 2 users, primary (A) and secondary (B), and the secondary user
98 * has a work profile running as another user (B2). Lets say that user B opens the phone settings
99 * (not currently supported, but theoretically speaking), and phone settings queries for a phone
Santos Cordon9c30c282015-05-13 16:28:27 -0700100 * account list. Lets also say that an app running in the work profile has registered a phone
101 * account. This means that:
Santos Cordon6a212642015-05-08 16:35:23 -0700102 *
103 * Since phone settings always runs as the primary user, We have the following situation:
Santos Cordon9c30c282015-05-13 16:28:27 -0700104 * User A (settings) is requesting a list of phone accounts while the active user is User B, and
105 * that list contains a phone account for profile User B2.
Santos Cordon6a212642015-05-08 16:35:23 -0700106 *
107 * In practice, (2) is stored with the phone account handle and is part of the handle's ID. (1) is
108 * saved in {@link #mCurrentUserHandle} and (3) we get from Binder.getCallingUser(). We check these
109 * users for visibility before returning any phone accounts.
Santos Cordon176ae282014-07-14 02:02:14 -0700110 */
Ihab Awadb78b2762014-07-25 15:16:23 -0700111public final class PhoneAccountRegistrar {
Santos Cordon176ae282014-07-14 02:02:14 -0700112
Yorke Lee5e8836a2014-08-22 15:25:18 -0700113 public static final PhoneAccountHandle NO_ACCOUNT_SELECTED =
114 new PhoneAccountHandle(new ComponentName("null", "null"), "NO_ACCOUNT_SELECTED");
115
Ihab Awadb78b2762014-07-25 15:16:23 -0700116 public abstract static class Listener {
117 public void onAccountsChanged(PhoneAccountRegistrar registrar) {}
118 public void onDefaultOutgoingChanged(PhoneAccountRegistrar registrar) {}
119 public void onSimCallManagerChanged(PhoneAccountRegistrar registrar) {}
120 }
121
122 private static final String FILE_NAME = "phone-account-registrar-state.xml";
Tyler Gunn84253572014-09-02 14:50:05 -0700123 @VisibleForTesting
Roshan Pius6f752c82015-08-10 10:42:06 -0700124 public static final int EXPECTED_STATE_VERSION = 8;
Tyler Gunn84253572014-09-02 14:50:05 -0700125
126 /** Keep in sync with the same in SipSettings.java */
127 private static final String SIP_SHARED_PREFERENCES = "SIP_PREFERENCES";
Ihab Awadb78b2762014-07-25 15:16:23 -0700128
129 private final List<Listener> mListeners = new CopyOnWriteArrayList<>();
130 private final AtomicFile mAtomicFile;
Santos Cordonafe59e52014-08-22 16:48:43 -0700131 private final Context mContext;
Evan Charlton105d9772014-11-25 14:08:53 -0800132 private final UserManager mUserManager;
Wink Saville33c05d32014-11-20 13:04:17 -0800133 private final SubscriptionManager mSubscriptionManager;
Ihab Awadb78b2762014-07-25 15:16:23 -0700134 private State mState;
Evan Charlton105d9772014-11-25 14:08:53 -0800135 private UserHandle mCurrentUserHandle;
Santos Cordon176ae282014-07-14 02:02:14 -0700136
Nancy Chen06ce0622014-10-23 01:17:35 +0000137 @VisibleForTesting
Ihab Awad26923222014-07-30 10:54:35 -0700138 public PhoneAccountRegistrar(Context context) {
Ihab Awadb78b2762014-07-25 15:16:23 -0700139 this(context, FILE_NAME);
140 }
141
142 @VisibleForTesting
143 public PhoneAccountRegistrar(Context context, String fileName) {
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700144 // TODO: This file path is subject to change -- it is storing the phone account registry
145 // state file in the path /data/system/users/0/, which is likely not correct in a
146 // multi-user setting.
147 /** UNCOMMENT_FOR_MOVE_TO_SYSTEM_SERVICE
148 String filePath = Environment.getUserSystemDirectory(UserHandle.myUserId()).
149 getAbsolutePath();
150 mAtomicFile = new AtomicFile(new File(filePath, fileName));
151 UNCOMMENT_FOR_MOVE_TO_SYSTEM_SERVICE */
Ihab Awadb78b2762014-07-25 15:16:23 -0700152 mAtomicFile = new AtomicFile(new File(context.getFilesDir(), fileName));
Tyler Gunn91d43cf2014-09-17 12:19:39 -0700153
Ihab Awadb78b2762014-07-25 15:16:23 -0700154 mState = new State();
Santos Cordonafe59e52014-08-22 16:48:43 -0700155 mContext = context;
Evan Charlton105d9772014-11-25 14:08:53 -0800156 mUserManager = UserManager.get(context);
Wink Saville33c05d32014-11-20 13:04:17 -0800157 mSubscriptionManager = SubscriptionManager.from(mContext);
Evan Charlton105d9772014-11-25 14:08:53 -0800158 mCurrentUserHandle = Process.myUserHandle();
Ihab Awadb78b2762014-07-25 15:16:23 -0700159 read();
Santos Cordon176ae282014-07-14 02:02:14 -0700160 }
161
Tyler Gunn84253572014-09-02 14:50:05 -0700162 /**
Nancy Chen140004a2014-10-15 15:48:38 -0700163 * Retrieves the subscription id for a given phone account if it exists. Subscription ids
164 * apply only to PSTN/SIM card phone accounts so all other accounts should not have a
165 * subscription id.
166 * @param accountHandle The handle for the phone account for which to retrieve the
167 * subscription id.
Wink Saville35850602014-10-23 15:57:21 -0700168 * @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 -0700169 */
Wink Saville35850602014-10-23 15:57:21 -0700170 public int getSubscriptionIdForPhoneAccount(PhoneAccountHandle accountHandle) {
Santos Cordon6a212642015-05-08 16:35:23 -0700171 PhoneAccount account = getPhoneAccountCheckCallingUser(accountHandle);
Santos Cordon479b3022015-02-06 04:27:15 -0800172
Santos Cordon6a212642015-05-08 16:35:23 -0700173 if (account != null && account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
Santos Cordon479b3022015-02-06 04:27:15 -0800174 TelephonyManager tm =
175 (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
176 return tm.getSubIdForPhoneAccount(account);
Nancy Chen140004a2014-10-15 15:48:38 -0700177 }
Santos Cordon479b3022015-02-06 04:27:15 -0800178 return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
Nancy Chen140004a2014-10-15 15:48:38 -0700179 }
180
181 /**
Evan Charlton105d9772014-11-25 14:08:53 -0800182 * Retrieves the default outgoing phone account supporting the specified uriScheme. Note that if
183 * {@link #mCurrentUserHandle} does not have visibility into the current default, {@code null}
184 * will be returned.
185 *
Tyler Gunn84253572014-09-02 14:50:05 -0700186 * @param uriScheme The URI scheme for the outgoing call.
187 * @return The {@link PhoneAccountHandle} to use.
188 */
Santos Cordon6a212642015-05-08 16:35:23 -0700189 public PhoneAccountHandle getOutgoingPhoneAccountForScheme(String uriScheme) {
Yorke Lee5e8836a2014-08-22 15:25:18 -0700190 final PhoneAccountHandle userSelected = getUserSelectedOutgoingPhoneAccount();
Tyler Gunn84253572014-09-02 14:50:05 -0700191
Yorke Lee5e8836a2014-08-22 15:25:18 -0700192 if (userSelected != null) {
Tyler Gunn84253572014-09-02 14:50:05 -0700193 // If there is a default PhoneAccount, ensure it supports calls to handles with the
194 // specified uriScheme.
Santos Cordon6a212642015-05-08 16:35:23 -0700195 final PhoneAccount userSelectedAccount = getPhoneAccountCheckCallingUser(userSelected);
196 if (userSelectedAccount.supportsUriScheme(uriScheme)) {
Tyler Gunn84253572014-09-02 14:50:05 -0700197 return userSelected;
198 }
Ihab Awad293edf22014-07-24 17:52:29 -0700199 }
200
Santos Cordonea5cb932015-05-07 16:28:38 -0700201 List<PhoneAccountHandle> outgoing = getCallCapablePhoneAccounts(uriScheme, false);
Ihab Awad6fb37c82014-08-07 19:48:57 -0700202 switch (outgoing.size()) {
Ihab Awad293edf22014-07-24 17:52:29 -0700203 case 0:
204 // There are no accounts, so there can be no default
205 return null;
206 case 1:
Evan Charlton105d9772014-11-25 14:08:53 -0800207 // There is only one account, which is by definition the default.
Santos Cordon6a212642015-05-08 16:35:23 -0700208 return outgoing.get(0);
Ihab Awad293edf22014-07-24 17:52:29 -0700209 default:
210 // There are multiple accounts with no selected default
211 return null;
Ihab Awadf2a84912014-07-22 21:09:25 -0700212 }
Ihab Awad104f8062014-07-17 11:29:35 -0700213 }
Santos Cordon176ae282014-07-14 02:02:14 -0700214
Evan Charlton105d9772014-11-25 14:08:53 -0800215 /**
216 * @return The user-selected outgoing {@link PhoneAccount}, or null if it hasn't been set (or
217 * if it was set by another user).
218 */
Yorke Lee5e8836a2014-08-22 15:25:18 -0700219 PhoneAccountHandle getUserSelectedOutgoingPhoneAccount() {
Santos Cordon6a212642015-05-08 16:35:23 -0700220 PhoneAccount account = getPhoneAccountCheckCallingUser(mState.defaultOutgoing);
221 if (account != null) {
222 return mState.defaultOutgoing;
Yorke Lee5e8836a2014-08-22 15:25:18 -0700223 }
224 return null;
225 }
226
Santos Cordon6a212642015-05-08 16:35:23 -0700227 /**
228 * Sets the phone account with which to place all calls by default. Set by the user
229 * within phone settings.
230 */
Andrew Leea51a3862014-09-03 14:58:45 -0700231 public void setUserSelectedOutgoingPhoneAccount(PhoneAccountHandle accountHandle) {
Evan Charlton89176372014-07-19 18:23:09 -0700232 if (accountHandle == null) {
Ihab Awad104f8062014-07-17 11:29:35 -0700233 // Asking to clear the default outgoing is a valid request
Ihab Awad293edf22014-07-24 17:52:29 -0700234 mState.defaultOutgoing = null;
Ihab Awad104f8062014-07-17 11:29:35 -0700235 } else {
Santos Cordon6a212642015-05-08 16:35:23 -0700236 // TODO: Do we really want to return for *any* user?
237 PhoneAccount account = getPhoneAccount(accountHandle);
238 if (account == null) {
Ihab Awadb78b2762014-07-25 15:16:23 -0700239 Log.w(this, "Trying to set nonexistent default outgoing %s",
240 accountHandle);
241 return;
242 }
243
Santos Cordon6a212642015-05-08 16:35:23 -0700244 if (!account.hasCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)) {
Ihab Awadb78b2762014-07-25 15:16:23 -0700245 Log.w(this, "Trying to set non-call-provider default outgoing %s",
Evan Charlton89176372014-07-19 18:23:09 -0700246 accountHandle);
Ihab Awad104f8062014-07-17 11:29:35 -0700247 return;
248 }
249
Santos Cordon6a212642015-05-08 16:35:23 -0700250 if (account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
Nancy Chen5a36b6e2014-10-23 17:42:42 -0700251 // If the account selected is a SIM account, propagate down to the subscription
252 // record.
Wink Saville7ce6e782014-10-27 10:56:46 -0700253 int subId = getSubscriptionIdForPhoneAccount(accountHandle);
Wink Saville33c05d32014-11-20 13:04:17 -0800254 mSubscriptionManager.setDefaultVoiceSubId(subId);
Nancy Chen5a36b6e2014-10-23 17:42:42 -0700255 }
256
Ihab Awad293edf22014-07-24 17:52:29 -0700257 mState.defaultOutgoing = accountHandle;
Santos Cordon176ae282014-07-14 02:02:14 -0700258 }
259
Ihab Awad293edf22014-07-24 17:52:29 -0700260 write();
Ihab Awadb78b2762014-07-25 15:16:23 -0700261 fireDefaultOutgoingChanged();
Santos Cordon176ae282014-07-14 02:02:14 -0700262 }
263
Nancy Chen668dee02014-11-19 15:31:31 -0800264 boolean isUserSelectedSmsPhoneAccount(PhoneAccountHandle accountHandle) {
265 return getSubscriptionIdForPhoneAccount(accountHandle) ==
266 SubscriptionManager.getDefaultSmsSubId();
267 }
268
Evan Charlton105d9772014-11-25 14:08:53 -0800269 /**
Santos Cordon59c21a72015-06-11 10:11:21 -0700270 * Returns the {@link PhoneAccountHandle} corresponding to the currently active SIM Call
271 * Manager. SIM Call Manager returned corresponds to the following priority order:
272 * 1. If a SIM Call Manager {@link PhoneAccount} is registered for the same package as the
273 * default dialer, then that one is returned.
274 * 2. If there is a SIM Call Manager {@link PhoneAccount} registered which matches the
275 * carrier configuration's default, then that one is returned.
276 * 3. Otherwise, we return null.
Evan Charlton105d9772014-11-25 14:08:53 -0800277 */
Ihab Awad293edf22014-07-24 17:52:29 -0700278 public PhoneAccountHandle getSimCallManager() {
Sailesh Nepalfc43ea82015-07-28 19:30:28 -0700279 long token = Binder.clearCallingIdentity();
280 int user;
281 try {
282 user = ActivityManager.getCurrentUser();
283 } finally {
284 Binder.restoreCallingIdentity(token);
285 }
286 return getSimCallManager(user);
287 }
288
289 /**
290 * Returns the {@link PhoneAccountHandle} corresponding to the currently active SIM Call
291 * Manager. SIM Call Manager returned corresponds to the following priority order:
292 * 1. If a SIM Call Manager {@link PhoneAccount} is registered for the same package as the
293 * default dialer, then that one is returned.
294 * 2. If there is a SIM Call Manager {@link PhoneAccount} registered which matches the
295 * carrier configuration's default, then that one is returned.
296 * 3. Otherwise, we return null.
297 */
298 public PhoneAccountHandle getSimCallManager(int user) {
Santos Cordon59c21a72015-06-11 10:11:21 -0700299 // Get the default dialer in case it has a connection manager associated with it.
Sailesh Nepalfc43ea82015-07-28 19:30:28 -0700300 String dialerPackage = DefaultDialerManager.getDefaultDialerApplication(mContext, user);
Evan Charltonaf51ceb2014-07-30 11:56:36 -0700301
Sailesh Nepalaecfa9b2015-06-16 14:55:27 -0700302 // Check carrier config.
303 String defaultSimCallManager = null;
304 CarrierConfigManager configManager = (CarrierConfigManager) mContext.getSystemService(
305 Context.CARRIER_CONFIG_SERVICE);
306 PersistableBundle configBundle = configManager.getConfig();
307 if (configBundle != null) {
308 defaultSimCallManager = configBundle.getString(
309 CarrierConfigManager.KEY_DEFAULT_SIM_CALL_MANAGER_STRING);
310 }
311
Santos Cordon59c21a72015-06-11 10:11:21 -0700312 ComponentName systemSimCallManagerComponent = TextUtils.isEmpty(defaultSimCallManager) ?
313 null : ComponentName.unflattenFromString(defaultSimCallManager);
Sailesh Nepal91fc8092015-02-14 15:44:55 -0800314
Santos Cordon59c21a72015-06-11 10:11:21 -0700315 PhoneAccountHandle dialerSimCallManager = null;
316 PhoneAccountHandle systemSimCallManager = null;
Sailesh Nepal91fc8092015-02-14 15:44:55 -0800317
Santos Cordon59c21a72015-06-11 10:11:21 -0700318 if (!TextUtils.isEmpty(dialerPackage) || systemSimCallManagerComponent != null) {
319 // loop through and look for any connection manager in the same package.
320 List<PhoneAccountHandle> allSimCallManagers = getPhoneAccountHandles(
321 PhoneAccount.CAPABILITY_CONNECTION_MANAGER, null, null,
322 true /* includeDisabledAccounts */);
323 for (PhoneAccountHandle accountHandle : allSimCallManagers) {
324 ComponentName component = accountHandle.getComponentName();
325
326 // Store the system connection manager if found
327 if (systemSimCallManager == null
328 && Objects.equals(component, systemSimCallManagerComponent)
329 && !resolveComponent(accountHandle).isEmpty()) {
330 systemSimCallManager = accountHandle;
331
332 // Store the dialer connection manager if found
333 } else if (dialerSimCallManager == null
334 && Objects.equals(component.getPackageName(), dialerPackage)
335 && !resolveComponent(accountHandle).isEmpty()) {
336 dialerSimCallManager = accountHandle;
Evan Charltonaf51ceb2014-07-30 11:56:36 -0700337 }
Evan Charltonaf51ceb2014-07-30 11:56:36 -0700338 }
Evan Charltonaf51ceb2014-07-30 11:56:36 -0700339 }
340
Santos Cordon59c21a72015-06-11 10:11:21 -0700341 PhoneAccountHandle retval = dialerSimCallManager != null ?
342 dialerSimCallManager : systemSimCallManager;
343
344 Log.i(this, "SimCallManager queried, returning: %s", retval);
345
Sailesh Nepalb3ccd7e2015-06-26 18:16:18 -0700346 return retval;
Ihab Awad293edf22014-07-24 17:52:29 -0700347 }
348
Evan Charlton105d9772014-11-25 14:08:53 -0800349 /**
Evan Charlton105d9772014-11-25 14:08:53 -0800350 * Update the current UserHandle to track when users are switched. This will allow the
351 * PhoneAccountRegistar to self-filter the PhoneAccounts to make sure we don't leak anything
352 * across users.
Santos Cordon6a212642015-05-08 16:35:23 -0700353 * We cannot simply check the calling user because that would always return the primary user for
354 * all invocations originating with the system process.
Evan Charlton105d9772014-11-25 14:08:53 -0800355 *
356 * @param userHandle The {@link UserHandle}, as delivered by
357 * {@link Intent#ACTION_USER_SWITCHED}.
358 */
359 public void setCurrentUserHandle(UserHandle userHandle) {
360 if (userHandle == null) {
361 Log.d(this, "setCurrentUserHandle, userHandle = null");
362 userHandle = Process.myUserHandle();
363 }
364 Log.d(this, "setCurrentUserHandle, %s", userHandle);
365 mCurrentUserHandle = userHandle;
366 }
367
Yorke Lee71734c22015-06-02 14:22:56 -0700368 /**
369 * @return {@code true} if the phone account was successfully enabled/disabled, {@code false}
370 * otherwise.
371 */
372 public boolean enablePhoneAccount(PhoneAccountHandle accountHandle, boolean isEnabled) {
Santos Cordonea5cb932015-05-07 16:28:38 -0700373 PhoneAccount account = getPhoneAccount(accountHandle);
Santos Cordon638f05c2015-05-19 17:12:33 -0700374 if (account == null) {
375 Log.w(this, "Could not find account to enable: " + accountHandle);
Yorke Lee71734c22015-06-02 14:22:56 -0700376 return false;
Santos Cordon638f05c2015-05-19 17:12:33 -0700377 } else if (account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
Santos Cordonea5cb932015-05-07 16:28:38 -0700378 // We never change the enabled state of SIM-based accounts.
Santos Cordon638f05c2015-05-19 17:12:33 -0700379 Log.w(this, "Could not change enable state of SIM account: " + accountHandle);
Yorke Lee71734c22015-06-02 14:22:56 -0700380 return false;
Santos Cordonea5cb932015-05-07 16:28:38 -0700381 }
382
Santos Cordon638f05c2015-05-19 17:12:33 -0700383 if (account.isEnabled() != isEnabled) {
Santos Cordonea5cb932015-05-07 16:28:38 -0700384 account.setIsEnabled(isEnabled);
385 write();
386 fireAccountsChanged();
387 }
Yorke Lee71734c22015-06-02 14:22:56 -0700388 return true;
Santos Cordonea5cb932015-05-07 16:28:38 -0700389 }
390
Evan Charlton105d9772014-11-25 14:08:53 -0800391 private boolean isVisibleForUser(PhoneAccount account) {
392 if (account == null) {
393 return false;
394 }
395
396 // If this PhoneAccount has CAPABILITY_MULTI_USER, it should be visible to all users and
397 // all profiles. Only Telephony and SIP accounts should have this capability.
398 if (account.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
399 return true;
400 }
401
402 UserHandle phoneAccountUserHandle = account.getAccountHandle().getUserHandle();
403 if (phoneAccountUserHandle == null) {
404 return false;
405 }
406
407 if (mCurrentUserHandle == null) {
408 Log.d(this, "Current user is null; assuming true");
409 return true;
410 }
411
Sailesh Nepal91fc8092015-02-14 15:44:55 -0800412 if (phoneAccountUserHandle.equals(Binder.getCallingUserHandle())) {
413 return true;
414 }
415
Santos Cordon6a212642015-05-08 16:35:23 -0700416 // Special check for work profiles.
Evan Charlton105d9772014-11-25 14:08:53 -0800417 // Unlike in TelecomServiceImpl, we only care about *profiles* here. We want to make sure
418 // that we don't resolve PhoneAccount across *users*, but resolving across *profiles* is
419 // fine.
Sailesh Nepal91fc8092015-02-14 15:44:55 -0800420 if (UserHandle.getCallingUserId() == UserHandle.USER_OWNER) {
421 List<UserInfo> profileUsers =
422 mUserManager.getProfiles(mCurrentUserHandle.getIdentifier());
423 for (UserInfo profileInfo : profileUsers) {
424 if (profileInfo.getUserHandle().equals(phoneAccountUserHandle)) {
425 return true;
426 }
Evan Charlton105d9772014-11-25 14:08:53 -0800427 }
428 }
Sailesh Nepal91fc8092015-02-14 15:44:55 -0800429
Evan Charlton105d9772014-11-25 14:08:53 -0800430 return false;
431 }
432
433 private List<ResolveInfo> resolveComponent(PhoneAccountHandle phoneAccountHandle) {
434 return resolveComponent(phoneAccountHandle.getComponentName(),
435 phoneAccountHandle.getUserHandle());
436 }
437
438 private List<ResolveInfo> resolveComponent(ComponentName componentName,
439 UserHandle userHandle) {
mike dooley10a58312014-11-06 13:46:19 -0800440 PackageManager pm = mContext.getPackageManager();
441 Intent intent = new Intent(ConnectionService.SERVICE_INTERFACE);
442 intent.setComponent(componentName);
Sailesh Nepal91fc8092015-02-14 15:44:55 -0800443 try {
444 if (userHandle != null) {
445 return pm.queryIntentServicesAsUser(intent, 0, userHandle.getIdentifier());
446 } else {
447 return pm.queryIntentServices(intent, 0);
448 }
449 } catch (SecurityException e) {
Santos Cordon3188b362015-05-22 13:01:10 -0700450 Log.e(this, e, "%s is not visible for the calling user", componentName);
Sailesh Nepal91fc8092015-02-14 15:44:55 -0800451 return Collections.EMPTY_LIST;
Evan Charlton105d9772014-11-25 14:08:53 -0800452 }
mike dooley10a58312014-11-06 13:46:19 -0800453 }
454
Tyler Gunn8e0fef42014-09-08 18:34:44 -0700455 /**
456 * Retrieves a list of all {@link PhoneAccountHandle}s registered.
Santos Cordonea5cb932015-05-07 16:28:38 -0700457 * Only returns accounts which are enabled.
Tyler Gunn8e0fef42014-09-08 18:34:44 -0700458 *
459 * @return The list of {@link PhoneAccountHandle}s.
460 */
Ihab Awad293edf22014-07-24 17:52:29 -0700461 public List<PhoneAccountHandle> getAllPhoneAccountHandles() {
Santos Cordonea5cb932015-05-07 16:28:38 -0700462 return getPhoneAccountHandles(0, null, null, false);
Ihab Awad293edf22014-07-24 17:52:29 -0700463 }
464
465 public List<PhoneAccount> getAllPhoneAccounts() {
Santos Cordonea5cb932015-05-07 16:28:38 -0700466 return getPhoneAccounts(0, null, null, false);
Santos Cordonafe59e52014-08-22 16:48:43 -0700467 }
468
Tyler Gunn8e0fef42014-09-08 18:34:44 -0700469 /**
Nancy Chen309198e2014-09-15 18:02:49 -0700470 * Retrieves a list of all phone account call provider phone accounts supporting the
Tyler Gunn8e0fef42014-09-08 18:34:44 -0700471 * specified URI scheme.
472 *
473 * @param uriScheme The URI scheme.
474 * @return The phone account handles.
475 */
Santos Cordonea5cb932015-05-07 16:28:38 -0700476 public List<PhoneAccountHandle> getCallCapablePhoneAccounts(
477 String uriScheme, boolean includeDisabledAccounts) {
478 return getPhoneAccountHandles(
479 PhoneAccount.CAPABILITY_CALL_PROVIDER, uriScheme, null, includeDisabledAccounts);
Santos Cordon6a212642015-05-08 16:35:23 -0700480 }
481
482 /**
483 * Retrieves a list of all the SIM-based phone accounts.
484 */
485 public List<PhoneAccountHandle> getSimPhoneAccounts() {
486 return getPhoneAccountHandles(
487 PhoneAccount.CAPABILITY_CALL_PROVIDER | PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION,
Santos Cordonea5cb932015-05-07 16:28:38 -0700488 null, null, false);
Tyler Gunn84253572014-09-02 14:50:05 -0700489 }
490
Tyler Gunn8e0fef42014-09-08 18:34:44 -0700491 /**
Nancy Chen1c5926f2014-09-17 14:44:14 -0700492 * Retrieves a list of all phone accounts registered by a specified package.
493 *
494 * @param packageName The name of the package that registered the phone accounts.
495 * @return The phone account handles.
496 */
497 public List<PhoneAccountHandle> getPhoneAccountsForPackage(String packageName) {
Santos Cordonea5cb932015-05-07 16:28:38 -0700498 return getPhoneAccountHandles(0, null, packageName, false);
Nancy Chen1c5926f2014-09-17 14:44:14 -0700499 }
500
Ihab Awad104f8062014-07-17 11:29:35 -0700501 // TODO: Should we implement an artificial limit for # of accounts associated with a single
502 // ComponentName?
Ihab Awad293edf22014-07-24 17:52:29 -0700503 public void registerPhoneAccount(PhoneAccount account) {
Tyler Gunncb59b672014-08-20 09:02:11 -0700504 // Enforce the requirement that a connection service for a phone account has the correct
505 // permission.
Santos Cordon6a212642015-05-08 16:35:23 -0700506 if (!phoneAccountRequiresBindPermission(account.getAccountHandle())) {
Yorke Lee7bb8ce92015-05-13 16:28:29 -0700507 Log.w(this,
508 "Phone account %s does not have BIND_TELECOM_CONNECTION_SERVICE permission.",
Tyler Gunncb59b672014-08-20 09:02:11 -0700509 account.getAccountHandle());
Yorke Lee7bb8ce92015-05-13 16:28:29 -0700510 throw new SecurityException("PhoneAccount connection service requires "
511 + "BIND_TELECOM_CONNECTION_SERVICE permission.");
Tyler Gunncb59b672014-08-20 09:02:11 -0700512 }
513
Tyler Gunn8e0fef42014-09-08 18:34:44 -0700514 addOrReplacePhoneAccount(account);
515 }
516
517 /**
518 * Adds a {@code PhoneAccount}, replacing an existing one if found.
519 *
520 * @param account The {@code PhoneAccount} to add or replace.
521 */
522 private void addOrReplacePhoneAccount(PhoneAccount account) {
Ihab Awad7e2c7f32014-11-03 09:49:45 -0800523 Log.d(this, "addOrReplacePhoneAccount(%s -> %s)",
524 account.getAccountHandle(), account);
525
Santos Cordonea5cb932015-05-07 16:28:38 -0700526 // Start _enabled_ property as false.
527 // !!! IMPORTANT !!! It is important that we do not read the enabled state that the
528 // source app provides or else an third party app could enable itself.
529 boolean isEnabled = false;
530
Santos Cordon6a212642015-05-08 16:35:23 -0700531 PhoneAccount oldAccount = getPhoneAccount(account.getAccountHandle());
532 if (oldAccount != null) {
533 mState.accounts.remove(oldAccount);
Santos Cordonea5cb932015-05-07 16:28:38 -0700534 isEnabled = oldAccount.isEnabled();
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700535 Log.i(this, getAccountDiffString(account, oldAccount));
536 } else {
537 Log.i(this, "New phone account registered: " + account);
Santos Cordon176ae282014-07-14 02:02:14 -0700538 }
Santos Cordonea5cb932015-05-07 16:28:38 -0700539
Santos Cordon6a212642015-05-08 16:35:23 -0700540 mState.accounts.add(account);
Santos Cordonea5cb932015-05-07 16:28:38 -0700541 // Reset enabled state to whatever the value was if the account was already registered,
542 // or _true_ if this is a SIM-based account. All SIM-based accounts are always enabled.
543 account.setIsEnabled(
544 isEnabled || account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION));
Santos Cordon176ae282014-07-14 02:02:14 -0700545
Ihab Awad293edf22014-07-24 17:52:29 -0700546 write();
Ihab Awadb78b2762014-07-25 15:16:23 -0700547 fireAccountsChanged();
Ihab Awad293edf22014-07-24 17:52:29 -0700548 }
549
Evan Charlton89176372014-07-19 18:23:09 -0700550 public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) {
Santos Cordon6a212642015-05-08 16:35:23 -0700551 PhoneAccount account = getPhoneAccount(accountHandle);
552 if (account != null) {
553 if (mState.accounts.remove(account)) {
554 write();
555 fireAccountsChanged();
Ihab Awad104f8062014-07-17 11:29:35 -0700556 }
557 }
Santos Cordon176ae282014-07-14 02:02:14 -0700558 }
559
Tyler Gunnd900ce62014-08-13 11:40:59 -0700560 /**
561 * Un-registers all phone accounts associated with a specified package.
562 *
563 * @param packageName The package for which phone accounts will be removed.
Evan Charlton105d9772014-11-25 14:08:53 -0800564 * @param userHandle The {@link UserHandle} the package is running under.
Tyler Gunnd900ce62014-08-13 11:40:59 -0700565 */
Evan Charlton105d9772014-11-25 14:08:53 -0800566 public void clearAccounts(String packageName, UserHandle userHandle) {
Tyler Gunnd900ce62014-08-13 11:40:59 -0700567 boolean accountsRemoved = false;
568 Iterator<PhoneAccount> it = mState.accounts.iterator();
569 while (it.hasNext()) {
570 PhoneAccount phoneAccount = it.next();
Evan Charlton105d9772014-11-25 14:08:53 -0800571 PhoneAccountHandle handle = phoneAccount.getAccountHandle();
572 if (Objects.equals(packageName, handle.getComponentName().getPackageName())
573 && Objects.equals(userHandle, handle.getUserHandle())) {
Tyler Gunnd900ce62014-08-13 11:40:59 -0700574 Log.i(this, "Removing phone account " + phoneAccount.getLabel());
Shriram Ganesh8f89f8d2015-04-15 12:35:43 +0530575 mState.accounts.remove(phoneAccount);
Tyler Gunnd900ce62014-08-13 11:40:59 -0700576 accountsRemoved = true;
Ihab Awad104f8062014-07-17 11:29:35 -0700577 }
578 }
579
Tyler Gunnd900ce62014-08-13 11:40:59 -0700580 if (accountsRemoved) {
581 write();
582 fireAccountsChanged();
583 }
Ihab Awadb78b2762014-07-25 15:16:23 -0700584 }
585
Nancy Chen140004a2014-10-15 15:48:38 -0700586 public boolean isVoiceMailNumber(PhoneAccountHandle accountHandle, String number) {
Wink Saville35850602014-10-23 15:57:21 -0700587 int subId = getSubscriptionIdForPhoneAccount(accountHandle);
Yorke Leeb1957232015-06-30 10:09:54 -0700588 return PhoneNumberUtils.isVoiceMailNumber(mContext, subId, number);
Nancy Chen140004a2014-10-15 15:48:38 -0700589 }
590
Ihab Awadb78b2762014-07-25 15:16:23 -0700591 public void addListener(Listener l) {
592 mListeners.add(l);
593 }
594
595 public void removeListener(Listener l) {
Jay Shraunera82c8f72014-08-14 15:49:16 -0700596 if (l != null) {
597 mListeners.remove(l);
598 }
Ihab Awadb78b2762014-07-25 15:16:23 -0700599 }
600
601 private void fireAccountsChanged() {
602 for (Listener l : mListeners) {
603 l.onAccountsChanged(this);
604 }
605 }
606
607 private void fireDefaultOutgoingChanged() {
608 for (Listener l : mListeners) {
609 l.onDefaultOutgoingChanged(this);
610 }
611 }
612
613 private void fireSimCallManagerChanged() {
614 for (Listener l : mListeners) {
615 l.onSimCallManagerChanged(this);
616 }
Santos Cordon176ae282014-07-14 02:02:14 -0700617 }
618
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700619 private String getAccountDiffString(PhoneAccount account1, PhoneAccount account2) {
620 if (account1 == null || account2 == null) {
621 return "Diff: " + account1 + ", " + account2;
622 }
623
624 StringBuffer sb = new StringBuffer();
625 sb.append("[").append(account1.getAccountHandle());
Anthony Lee7c64a4e2015-08-25 09:27:47 -0700626 appendDiff(sb, "addr", Log.piiHandle(account1.getAddress()),
627 Log.piiHandle(account2.getAddress()));
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700628 appendDiff(sb, "cap", account1.getCapabilities(), account2.getCapabilities());
629 appendDiff(sb, "hl", account1.getHighlightColor(), account2.getHighlightColor());
630 appendDiff(sb, "icon", account1.getIcon(), account2.getIcon());
631 appendDiff(sb, "lbl", account1.getLabel(), account2.getLabel());
632 appendDiff(sb, "desc", account1.getShortDescription(), account2.getShortDescription());
Anthony Lee7c64a4e2015-08-25 09:27:47 -0700633 appendDiff(sb, "subAddr", Log.piiHandle(account1.getSubscriptionAddress()),
634 Log.piiHandle(account2.getSubscriptionAddress()));
Santos Cordon5fa4e4f2015-06-10 14:56:01 -0700635 appendDiff(sb, "uris", account1.getSupportedUriSchemes(),
636 account2.getSupportedUriSchemes());
637 sb.append("]");
638 return sb.toString();
639 }
640
641 private void appendDiff(StringBuffer sb, String attrName, Object obj1, Object obj2) {
642 if (!Objects.equals(obj1, obj2)) {
643 sb.append("(")
644 .append(attrName)
645 .append(": ")
646 .append(obj1)
647 .append(" -> ")
648 .append(obj2)
649 .append(")");
650 }
651 }
652
Tyler Gunncb59b672014-08-20 09:02:11 -0700653 /**
Santos Cordon6a212642015-05-08 16:35:23 -0700654 * Determines if the connection service specified by a {@link PhoneAccountHandle} requires the
Yorke Lee7bb8ce92015-05-13 16:28:29 -0700655 * {@link Manifest.permission#BIND_TELECOM_CONNECTION_SERVICE} permission.
Tyler Gunncb59b672014-08-20 09:02:11 -0700656 *
657 * @param phoneAccountHandle The phone account to check.
658 * @return {@code True} if the phone account has permission.
659 */
Santos Cordon6a212642015-05-08 16:35:23 -0700660 public boolean phoneAccountRequiresBindPermission(PhoneAccountHandle phoneAccountHandle) {
Sailesh Nepal91fc8092015-02-14 15:44:55 -0800661 List<ResolveInfo> resolveInfos = resolveComponent(phoneAccountHandle);
662 if (resolveInfos.isEmpty()) {
663 Log.w(this, "phoneAccount %s not found", phoneAccountHandle.getComponentName());
Tyler Gunncb59b672014-08-20 09:02:11 -0700664 return false;
665 }
Sailesh Nepal91fc8092015-02-14 15:44:55 -0800666 for (ResolveInfo resolveInfo : resolveInfos) {
667 ServiceInfo serviceInfo = resolveInfo.serviceInfo;
Yorke Lee7bb8ce92015-05-13 16:28:29 -0700668 if (serviceInfo == null) {
669 return false;
670 }
671
672 if (!Manifest.permission.BIND_CONNECTION_SERVICE.equals(serviceInfo.permission) &&
673 !Manifest.permission.BIND_TELECOM_CONNECTION_SERVICE.equals(
674 serviceInfo.permission)) {
675 // The ConnectionService must require either the deprecated BIND_CONNECTION_SERVICE,
676 // or the public BIND_TELECOM_CONNECTION_SERVICE permissions, both of which are
677 // system/signature only.
Sailesh Nepal91fc8092015-02-14 15:44:55 -0800678 return false;
679 }
680 }
681 return true;
Tyler Gunncb59b672014-08-20 09:02:11 -0700682 }
683
Santos Cordon6a212642015-05-08 16:35:23 -0700684 //
685 // Methods for retrieving PhoneAccounts and PhoneAccountHandles
686 //
Ihab Awad293edf22014-07-24 17:52:29 -0700687
Santos Cordonafe59e52014-08-22 16:48:43 -0700688 /**
Santos Cordon6a212642015-05-08 16:35:23 -0700689 * Returns the PhoneAccount for the specified handle. Does no user checking.
Tyler Gunn84253572014-09-02 14:50:05 -0700690 *
Santos Cordon6a212642015-05-08 16:35:23 -0700691 * @param handle
692 * @return The corresponding phone account if one exists.
Santos Cordonafe59e52014-08-22 16:48:43 -0700693 */
Santos Cordon6a212642015-05-08 16:35:23 -0700694 PhoneAccount getPhoneAccount(PhoneAccountHandle handle) {
695 for (PhoneAccount m : mState.accounts) {
696 if (Objects.equals(handle, m.getAccountHandle())) {
697 return m;
698 }
699 }
700 return null;
701 }
702
703 /**
704 * Like getPhoneAccount, but checks to see if the current user is allowed to see the phone
705 * account before returning it. The current user is the active user on the actual android
706 * device.
707 */
708 public PhoneAccount getPhoneAccountCheckCallingUser(PhoneAccountHandle handle) {
709 PhoneAccount account = getPhoneAccount(handle);
710 if (account != null && isVisibleForUser(account)) {
711 return account;
712 }
713 return null;
714 }
715
716 /**
717 * Returns a list of phone account handles with the specified capabilities, uri scheme,
718 * and package name.
719 */
720 private List<PhoneAccountHandle> getPhoneAccountHandles(
Santos Cordonea5cb932015-05-07 16:28:38 -0700721 int capabilities,
722 String uriScheme,
723 String packageName,
724 boolean includeDisabledAccounts) {
Santos Cordon6a212642015-05-08 16:35:23 -0700725 List<PhoneAccountHandle> handles = new ArrayList<>();
Santos Cordonea5cb932015-05-07 16:28:38 -0700726
727 for (PhoneAccount account : getPhoneAccounts(
728 capabilities, uriScheme, packageName, includeDisabledAccounts)) {
Santos Cordon6a212642015-05-08 16:35:23 -0700729 handles.add(account.getAccountHandle());
730 }
731 return handles;
Tyler Gunn84253572014-09-02 14:50:05 -0700732 }
733
734 /**
735 * Returns a list of phone account handles with the specified flag, supporting the specified
Santos Cordon6a212642015-05-08 16:35:23 -0700736 * URI scheme, within the specified package name.
Tyler Gunn84253572014-09-02 14:50:05 -0700737 *
Santos Cordon6a212642015-05-08 16:35:23 -0700738 * @param capabilities Capabilities which the {@code PhoneAccount} must have. Ignored if 0.
739 * @param uriScheme URI schemes the PhoneAccount must handle. {@code null} bypasses the
Tyler Gunn84253572014-09-02 14:50:05 -0700740 * URI scheme check.
Santos Cordon6a212642015-05-08 16:35:23 -0700741 * @param packageName Package name of the PhoneAccount. {@code null} bypasses packageName check.
Tyler Gunn84253572014-09-02 14:50:05 -0700742 */
Santos Cordon6a212642015-05-08 16:35:23 -0700743 private List<PhoneAccount> getPhoneAccounts(
Santos Cordonea5cb932015-05-07 16:28:38 -0700744 int capabilities,
745 String uriScheme,
746 String packageName,
747 boolean includeDisabledAccounts) {
Santos Cordon6a212642015-05-08 16:35:23 -0700748 List<PhoneAccount> accounts = new ArrayList<>(mState.accounts.size());
Ihab Awad293edf22014-07-24 17:52:29 -0700749 for (PhoneAccount m : mState.accounts) {
Santos Cordonea5cb932015-05-07 16:28:38 -0700750 if (!(m.isEnabled() || includeDisabledAccounts)) {
751 // Do not include disabled accounts.
752 continue;
753 }
754
Santos Cordon6a212642015-05-08 16:35:23 -0700755 if (capabilities != 0 && !m.hasCapabilities(capabilities)) {
Evan Charlton105d9772014-11-25 14:08:53 -0800756 // Account doesn't have the right capabilities; skip this one.
757 continue;
Ihab Awadf2a84912014-07-22 21:09:25 -0700758 }
Evan Charlton105d9772014-11-25 14:08:53 -0800759 if (uriScheme != null && !m.supportsUriScheme(uriScheme)) {
760 // Account doesn't support this URI scheme; skip this one.
761 continue;
762 }
Santos Cordon6a212642015-05-08 16:35:23 -0700763 PhoneAccountHandle handle = m.getAccountHandle();
764
765 if (resolveComponent(handle).isEmpty()) {
Evan Charlton105d9772014-11-25 14:08:53 -0800766 // This component cannot be resolved anymore; skip this one.
767 continue;
768 }
Santos Cordon6a212642015-05-08 16:35:23 -0700769 if (packageName != null &&
770 !packageName.equals(handle.getComponentName().getPackageName())) {
771 // Not the right package name; skip this one.
772 continue;
773 }
Evan Charlton105d9772014-11-25 14:08:53 -0800774 if (!isVisibleForUser(m)) {
775 // Account is not visible for the current user; skip this one.
776 continue;
777 }
Santos Cordon6a212642015-05-08 16:35:23 -0700778 accounts.add(m);
Ihab Awad104f8062014-07-17 11:29:35 -0700779 }
Santos Cordon6a212642015-05-08 16:35:23 -0700780 return accounts;
Ihab Awad104f8062014-07-17 11:29:35 -0700781 }
782
Santos Cordon6a212642015-05-08 16:35:23 -0700783 //
784 // State Implementation for PhoneAccountRegistrar
785 //
786
Ihab Awad293edf22014-07-24 17:52:29 -0700787 /**
788 * The state of this {@code PhoneAccountRegistrar}.
789 */
Ihab Awadb78b2762014-07-25 15:16:23 -0700790 @VisibleForTesting
791 public static class State {
Ihab Awad293edf22014-07-24 17:52:29 -0700792 /**
793 * The account selected by the user to be employed by default for making outgoing calls.
794 * If the user has not made such a selection, then this is null.
795 */
796 public PhoneAccountHandle defaultOutgoing = null;
797
798 /**
Tyler Gunn7cc70b42014-09-12 22:17:27 -0700799 * The complete list of {@code PhoneAccount}s known to the Telecom subsystem.
Ihab Awad293edf22014-07-24 17:52:29 -0700800 */
Shriram Ganesh8f89f8d2015-04-15 12:35:43 +0530801 public final List<PhoneAccount> accounts = new CopyOnWriteArrayList<>();
Tyler Gunn84253572014-09-02 14:50:05 -0700802
803 /**
804 * The version number of the State data.
805 */
806 public int versionNumber;
Ihab Awad293edf22014-07-24 17:52:29 -0700807 }
808
Tyler Gunn9787e0e2014-10-14 14:36:12 -0700809 /**
810 * Dumps the state of the {@link CallsManager}.
811 *
812 * @param pw The {@code IndentingPrintWriter} to write the state to.
813 */
814 public void dump(IndentingPrintWriter pw) {
815 if (mState != null) {
816 pw.println("xmlVersion: " + mState.versionNumber);
817 pw.println("defaultOutgoing: " + (mState.defaultOutgoing == null ? "none" :
818 mState.defaultOutgoing));
Santos Cordon59c21a72015-06-11 10:11:21 -0700819 pw.println("simCallManager: " + getSimCallManager());
Tyler Gunn9787e0e2014-10-14 14:36:12 -0700820 pw.println("phoneAccounts:");
821 pw.increaseIndent();
822 for (PhoneAccount phoneAccount : mState.accounts) {
823 pw.println(phoneAccount);
824 }
825 pw.decreaseIndent();
826 }
827 }
828
Ihab Awad293edf22014-07-24 17:52:29 -0700829 ////////////////////////////////////////////////////////////////////////////////////////////////
830 //
831 // State management
832 //
833
834 private void write() {
Ihab Awadb78b2762014-07-25 15:16:23 -0700835 final FileOutputStream os;
Ihab Awad104f8062014-07-17 11:29:35 -0700836 try {
Ihab Awadb78b2762014-07-25 15:16:23 -0700837 os = mAtomicFile.startWrite();
838 boolean success = false;
839 try {
840 XmlSerializer serializer = new FastXmlSerializer();
841 serializer.setOutput(new BufferedOutputStream(os), "utf-8");
Evan Charlton105d9772014-11-25 14:08:53 -0800842 writeToXml(mState, serializer, mContext);
Ihab Awadb78b2762014-07-25 15:16:23 -0700843 serializer.flush();
844 success = true;
845 } finally {
846 if (success) {
847 mAtomicFile.finishWrite(os);
848 } else {
849 mAtomicFile.failWrite(os);
850 }
851 }
852 } catch (IOException e) {
853 Log.e(this, e, "Writing state to XML file");
Ihab Awad104f8062014-07-17 11:29:35 -0700854 }
855 }
856
Ihab Awadb78b2762014-07-25 15:16:23 -0700857 private void read() {
858 final InputStream is;
Ihab Awad104f8062014-07-17 11:29:35 -0700859 try {
Ihab Awadb78b2762014-07-25 15:16:23 -0700860 is = mAtomicFile.openRead();
861 } catch (FileNotFoundException ex) {
862 return;
863 }
864
Tyler Gunn84253572014-09-02 14:50:05 -0700865 boolean versionChanged = false;
866
Ihab Awadb78b2762014-07-25 15:16:23 -0700867 XmlPullParser parser;
868 try {
869 parser = Xml.newPullParser();
870 parser.setInput(new BufferedInputStream(is), null);
871 parser.nextTag();
Tyler Gunn84253572014-09-02 14:50:05 -0700872 mState = readFromXml(parser, mContext);
873 versionChanged = mState.versionNumber < EXPECTED_STATE_VERSION;
874
Ihab Awadb78b2762014-07-25 15:16:23 -0700875 } catch (IOException | XmlPullParserException e) {
876 Log.e(this, e, "Reading state from XML file");
877 mState = new State();
878 } finally {
879 try {
880 is.close();
881 } catch (IOException e) {
882 Log.e(this, e, "Closing InputStream");
883 }
Ihab Awad104f8062014-07-17 11:29:35 -0700884 }
Tyler Gunn84253572014-09-02 14:50:05 -0700885
Evan Charlton105d9772014-11-25 14:08:53 -0800886 // Verify all of the UserHandles.
887 List<PhoneAccount> badAccounts = new ArrayList<>();
888 for (PhoneAccount phoneAccount : mState.accounts) {
889 UserHandle userHandle = phoneAccount.getAccountHandle().getUserHandle();
890 if (userHandle == null) {
891 Log.w(this, "Missing UserHandle for %s", phoneAccount);
892 badAccounts.add(phoneAccount);
893 } else if (mUserManager.getSerialNumberForUser(userHandle) == -1) {
894 Log.w(this, "User does not exist for %s", phoneAccount);
895 badAccounts.add(phoneAccount);
896 }
897 }
898 mState.accounts.removeAll(badAccounts);
899
Tyler Gunn84253572014-09-02 14:50:05 -0700900 // If an upgrade occurred, write out the changed data.
Evan Charlton105d9772014-11-25 14:08:53 -0800901 if (versionChanged || !badAccounts.isEmpty()) {
Tyler Gunn84253572014-09-02 14:50:05 -0700902 write();
903 }
Santos Cordon176ae282014-07-14 02:02:14 -0700904 }
905
Evan Charlton105d9772014-11-25 14:08:53 -0800906 private static void writeToXml(State state, XmlSerializer serializer, Context context)
Ihab Awadb78b2762014-07-25 15:16:23 -0700907 throws IOException {
Evan Charlton105d9772014-11-25 14:08:53 -0800908 sStateXml.writeToXml(state, serializer, context);
Santos Cordon176ae282014-07-14 02:02:14 -0700909 }
Ihab Awad104f8062014-07-17 11:29:35 -0700910
Tyler Gunn84253572014-09-02 14:50:05 -0700911 private static State readFromXml(XmlPullParser parser, Context context)
Ihab Awadb78b2762014-07-25 15:16:23 -0700912 throws IOException, XmlPullParserException {
Tyler Gunn84253572014-09-02 14:50:05 -0700913 State s = sStateXml.readFromXml(parser, 0, context);
Ihab Awadb78b2762014-07-25 15:16:23 -0700914 return s != null ? s : new State();
Ihab Awad104f8062014-07-17 11:29:35 -0700915 }
916
Ihab Awad293edf22014-07-24 17:52:29 -0700917 ////////////////////////////////////////////////////////////////////////////////////////////////
Ihab Awad104f8062014-07-17 11:29:35 -0700918 //
Ihab Awadb78b2762014-07-25 15:16:23 -0700919 // XML serialization
Ihab Awad104f8062014-07-17 11:29:35 -0700920 //
921
Ihab Awadb78b2762014-07-25 15:16:23 -0700922 @VisibleForTesting
Ihab Awad26923222014-07-30 10:54:35 -0700923 public abstract static class XmlSerialization<T> {
Tyler Gunn84253572014-09-02 14:50:05 -0700924 private static final String LENGTH_ATTRIBUTE = "length";
925 private static final String VALUE_TAG = "value";
926
Ihab Awadb78b2762014-07-25 15:16:23 -0700927 /**
928 * Write the supplied object to XML
929 */
Evan Charlton105d9772014-11-25 14:08:53 -0800930 public abstract void writeToXml(T o, XmlSerializer serializer, Context context)
Ihab Awad26923222014-07-30 10:54:35 -0700931 throws IOException;
Ihab Awadb78b2762014-07-25 15:16:23 -0700932
933 /**
934 * Read from the supplied XML into a new object, returning null in case of an
935 * unrecoverable schema mismatch or other data error. 'parser' must be already
936 * positioned at the first tag that is expected to have been emitted by this
937 * object's writeToXml(). This object tries to fail early without modifying
938 * 'parser' if it does not recognize the data it sees.
939 */
Tyler Gunn84253572014-09-02 14:50:05 -0700940 public abstract T readFromXml(XmlPullParser parser, int version, Context context)
Ihab Awad26923222014-07-30 10:54:35 -0700941 throws IOException, XmlPullParserException;
942
Ihab Awadd9f54382014-10-24 11:44:47 -0700943 protected void writeTextIfNonNull(String tagName, Object value, XmlSerializer serializer)
Ihab Awad26923222014-07-30 10:54:35 -0700944 throws IOException {
945 if (value != null) {
946 serializer.startTag(null, tagName);
947 serializer.text(Objects.toString(value));
948 serializer.endTag(null, tagName);
949 }
950 }
Tyler Gunn84253572014-09-02 14:50:05 -0700951
952 /**
953 * Serializes a string array.
954 *
955 * @param tagName The tag name for the string array.
956 * @param values The string values to serialize.
957 * @param serializer The serializer.
958 * @throws IOException
959 */
960 protected void writeStringList(String tagName, List<String> values,
961 XmlSerializer serializer)
962 throws IOException {
963
964 serializer.startTag(null, tagName);
965 if (values != null) {
966 serializer.attribute(null, LENGTH_ATTRIBUTE, Objects.toString(values.size()));
967 for (String toSerialize : values) {
968 serializer.startTag(null, VALUE_TAG);
969 if (toSerialize != null ){
970 serializer.text(toSerialize);
971 }
Tyler Gunn84253572014-09-02 14:50:05 -0700972 serializer.endTag(null, VALUE_TAG);
973 }
974 } else {
975 serializer.attribute(null, LENGTH_ATTRIBUTE, "0");
976 }
977 serializer.endTag(null, tagName);
Ihab Awadd9f54382014-10-24 11:44:47 -0700978 }
Tyler Gunn84253572014-09-02 14:50:05 -0700979
Santos Cordon9c30c282015-05-13 16:28:27 -0700980 protected void writeIconIfNonNull(String tagName, Icon value, XmlSerializer serializer)
Ihab Awadd9f54382014-10-24 11:44:47 -0700981 throws IOException {
Santos Cordon9c30c282015-05-13 16:28:27 -0700982 if (value != null) {
Ihab Awadd9f54382014-10-24 11:44:47 -0700983 ByteArrayOutputStream stream = new ByteArrayOutputStream();
Santos Cordon9c30c282015-05-13 16:28:27 -0700984 value.writeToStream(stream);
985 byte[] iconByteArray = stream.toByteArray();
986 String text = Base64.encodeToString(iconByteArray, 0, iconByteArray.length, 0);
Ihab Awadd9f54382014-10-24 11:44:47 -0700987
988 serializer.startTag(null, tagName);
989 serializer.text(text);
990 serializer.endTag(null, tagName);
991 }
Tyler Gunn84253572014-09-02 14:50:05 -0700992 }
993
Evan Charlton105d9772014-11-25 14:08:53 -0800994 protected void writeLong(String tagName, long value, XmlSerializer serializer)
995 throws IOException {
996 serializer.startTag(null, tagName);
997 serializer.text(Long.valueOf(value).toString());
998 serializer.endTag(null, tagName);
999 }
1000
Tyler Gunn84253572014-09-02 14:50:05 -07001001 /**
1002 * Reads a string array from the XML parser.
1003 *
1004 * @param parser The XML parser.
1005 * @return String array containing the parsed values.
1006 * @throws IOException Exception related to IO.
1007 * @throws XmlPullParserException Exception related to parsing.
1008 */
1009 protected List<String> readStringList(XmlPullParser parser)
1010 throws IOException, XmlPullParserException {
1011
1012 int length = Integer.parseInt(parser.getAttributeValue(null, LENGTH_ATTRIBUTE));
1013 List<String> arrayEntries = new ArrayList<String>(length);
1014 String value = null;
1015
1016 if (length == 0) {
1017 return arrayEntries;
1018 }
1019
1020 int outerDepth = parser.getDepth();
1021 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
1022 if (parser.getName().equals(VALUE_TAG)) {
Tyler Gunn8e0fef42014-09-08 18:34:44 -07001023 parser.next();
Tyler Gunn84253572014-09-02 14:50:05 -07001024 value = parser.getText();
1025 arrayEntries.add(value);
1026 }
1027 }
1028
1029 return arrayEntries;
1030 }
Ihab Awadd9f54382014-10-24 11:44:47 -07001031
Santos Cordon9c30c282015-05-13 16:28:27 -07001032 protected Bitmap readBitmap(XmlPullParser parser) {
Ihab Awadd9f54382014-10-24 11:44:47 -07001033 byte[] imageByteArray = Base64.decode(parser.getText(), 0);
1034 return BitmapFactory.decodeByteArray(imageByteArray, 0, imageByteArray.length);
1035 }
Santos Cordon9c30c282015-05-13 16:28:27 -07001036
1037 protected Icon readIcon(XmlPullParser parser) throws IOException {
1038 byte[] iconByteArray = Base64.decode(parser.getText(), 0);
1039 ByteArrayInputStream stream = new ByteArrayInputStream(iconByteArray);
1040 return Icon.createFromStream(stream);
1041 }
Ihab Awad104f8062014-07-17 11:29:35 -07001042 }
1043
Ihab Awadb78b2762014-07-25 15:16:23 -07001044 @VisibleForTesting
1045 public static final XmlSerialization<State> sStateXml =
1046 new XmlSerialization<State>() {
1047 private static final String CLASS_STATE = "phone_account_registrar_state";
Ihab Awad104f8062014-07-17 11:29:35 -07001048 private static final String DEFAULT_OUTGOING = "default_outgoing";
1049 private static final String ACCOUNTS = "accounts";
Tyler Gunn84253572014-09-02 14:50:05 -07001050 private static final String VERSION = "version";
Ihab Awad104f8062014-07-17 11:29:35 -07001051
1052 @Override
Evan Charlton105d9772014-11-25 14:08:53 -08001053 public void writeToXml(State o, XmlSerializer serializer, Context context)
Ihab Awadb78b2762014-07-25 15:16:23 -07001054 throws IOException {
Ihab Awad26923222014-07-30 10:54:35 -07001055 if (o != null) {
1056 serializer.startTag(null, CLASS_STATE);
Tyler Gunn84253572014-09-02 14:50:05 -07001057 serializer.attribute(null, VERSION, Objects.toString(EXPECTED_STATE_VERSION));
Ihab Awadb78b2762014-07-25 15:16:23 -07001058
Ihab Awad26923222014-07-30 10:54:35 -07001059 if (o.defaultOutgoing != null) {
1060 serializer.startTag(null, DEFAULT_OUTGOING);
Evan Charlton105d9772014-11-25 14:08:53 -08001061 sPhoneAccountHandleXml.writeToXml(o.defaultOutgoing, serializer, context);
Ihab Awad26923222014-07-30 10:54:35 -07001062 serializer.endTag(null, DEFAULT_OUTGOING);
1063 }
1064
Ihab Awad26923222014-07-30 10:54:35 -07001065 serializer.startTag(null, ACCOUNTS);
1066 for (PhoneAccount m : o.accounts) {
Evan Charlton105d9772014-11-25 14:08:53 -08001067 sPhoneAccountXml.writeToXml(m, serializer, context);
Ihab Awad26923222014-07-30 10:54:35 -07001068 }
1069 serializer.endTag(null, ACCOUNTS);
1070
1071 serializer.endTag(null, CLASS_STATE);
Ihab Awad293edf22014-07-24 17:52:29 -07001072 }
Ihab Awad104f8062014-07-17 11:29:35 -07001073 }
1074
1075 @Override
Tyler Gunn84253572014-09-02 14:50:05 -07001076 public State readFromXml(XmlPullParser parser, int version, Context context)
Ihab Awadb78b2762014-07-25 15:16:23 -07001077 throws IOException, XmlPullParserException {
1078 if (parser.getName().equals(CLASS_STATE)) {
1079 State s = new State();
Tyler Gunn84253572014-09-02 14:50:05 -07001080
1081 String rawVersion = parser.getAttributeValue(null, VERSION);
1082 s.versionNumber = TextUtils.isEmpty(rawVersion) ? 1 :
1083 Integer.parseInt(rawVersion);
1084
Ihab Awadb78b2762014-07-25 15:16:23 -07001085 int outerDepth = parser.getDepth();
1086 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
1087 if (parser.getName().equals(DEFAULT_OUTGOING)) {
1088 parser.nextTag();
Tyler Gunn84253572014-09-02 14:50:05 -07001089 s.defaultOutgoing = sPhoneAccountHandleXml.readFromXml(parser,
1090 s.versionNumber, context);
Ihab Awadb78b2762014-07-25 15:16:23 -07001091 } else if (parser.getName().equals(ACCOUNTS)) {
1092 int accountsDepth = parser.getDepth();
1093 while (XmlUtils.nextElementWithin(parser, accountsDepth)) {
Tyler Gunn84253572014-09-02 14:50:05 -07001094 PhoneAccount account = sPhoneAccountXml.readFromXml(parser,
1095 s.versionNumber, context);
1096
1097 if (account != null && s.accounts != null) {
Ihab Awadb78b2762014-07-25 15:16:23 -07001098 s.accounts.add(account);
1099 }
1100 }
Ihab Awad104f8062014-07-17 11:29:35 -07001101 }
1102 }
Ihab Awadb78b2762014-07-25 15:16:23 -07001103 return s;
Ihab Awad104f8062014-07-17 11:29:35 -07001104 }
Ihab Awadb78b2762014-07-25 15:16:23 -07001105 return null;
Ihab Awad104f8062014-07-17 11:29:35 -07001106 }
1107 };
1108
Ihab Awadb78b2762014-07-25 15:16:23 -07001109 @VisibleForTesting
1110 public static final XmlSerialization<PhoneAccount> sPhoneAccountXml =
1111 new XmlSerialization<PhoneAccount>() {
1112 private static final String CLASS_PHONE_ACCOUNT = "phone_account";
1113 private static final String ACCOUNT_HANDLE = "account_handle";
Andrew Lee7129f1c2014-09-04 11:55:07 -07001114 private static final String ADDRESS = "handle";
1115 private static final String SUBSCRIPTION_ADDRESS = "subscription_number";
Ihab Awad104f8062014-07-17 11:29:35 -07001116 private static final String CAPABILITIES = "capabilities";
1117 private static final String ICON_RES_ID = "icon_res_id";
Ihab Awadd9f54382014-10-24 11:44:47 -07001118 private static final String ICON_PACKAGE_NAME = "icon_package_name";
1119 private static final String ICON_BITMAP = "icon_bitmap";
Ihab Awad7e2c7f32014-11-03 09:49:45 -08001120 private static final String ICON_TINT = "icon_tint";
1121 private static final String HIGHLIGHT_COLOR = "highlight_color";
Ihab Awad104f8062014-07-17 11:29:35 -07001122 private static final String LABEL = "label";
1123 private static final String SHORT_DESCRIPTION = "short_description";
Tyler Gunn84253572014-09-02 14:50:05 -07001124 private static final String SUPPORTED_URI_SCHEMES = "supported_uri_schemes";
Santos Cordon9c30c282015-05-13 16:28:27 -07001125 private static final String ICON = "icon";
Santos Cordonea5cb932015-05-07 16:28:38 -07001126 private static final String ENABLED = "enabled";
Ihab Awad104f8062014-07-17 11:29:35 -07001127
1128 @Override
Evan Charlton105d9772014-11-25 14:08:53 -08001129 public void writeToXml(PhoneAccount o, XmlSerializer serializer, Context context)
Ihab Awadb78b2762014-07-25 15:16:23 -07001130 throws IOException {
Ihab Awad26923222014-07-30 10:54:35 -07001131 if (o != null) {
1132 serializer.startTag(null, CLASS_PHONE_ACCOUNT);
Ihab Awadb78b2762014-07-25 15:16:23 -07001133
Ihab Awad26923222014-07-30 10:54:35 -07001134 if (o.getAccountHandle() != null) {
1135 serializer.startTag(null, ACCOUNT_HANDLE);
Evan Charlton105d9772014-11-25 14:08:53 -08001136 sPhoneAccountHandleXml.writeToXml(o.getAccountHandle(), serializer, context);
Ihab Awad26923222014-07-30 10:54:35 -07001137 serializer.endTag(null, ACCOUNT_HANDLE);
1138 }
Ihab Awadb78b2762014-07-25 15:16:23 -07001139
Ihab Awadd9f54382014-10-24 11:44:47 -07001140 writeTextIfNonNull(ADDRESS, o.getAddress(), serializer);
1141 writeTextIfNonNull(SUBSCRIPTION_ADDRESS, o.getSubscriptionAddress(), serializer);
1142 writeTextIfNonNull(CAPABILITIES, Integer.toString(o.getCapabilities()), serializer);
Santos Cordon9c30c282015-05-13 16:28:27 -07001143 writeIconIfNonNull(ICON, o.getIcon(), serializer);
Ihab Awad7e2c7f32014-11-03 09:49:45 -08001144 writeTextIfNonNull(HIGHLIGHT_COLOR,
1145 Integer.toString(o.getHighlightColor()), serializer);
Ihab Awadd9f54382014-10-24 11:44:47 -07001146 writeTextIfNonNull(LABEL, o.getLabel(), serializer);
1147 writeTextIfNonNull(SHORT_DESCRIPTION, o.getShortDescription(), serializer);
Tyler Gunn84253572014-09-02 14:50:05 -07001148 writeStringList(SUPPORTED_URI_SCHEMES, o.getSupportedUriSchemes(), serializer);
Santos Cordonea5cb932015-05-07 16:28:38 -07001149 writeTextIfNonNull(ENABLED, o.isEnabled() ? "true" : "false" , serializer);
Ihab Awadb78b2762014-07-25 15:16:23 -07001150
Ihab Awad26923222014-07-30 10:54:35 -07001151 serializer.endTag(null, CLASS_PHONE_ACCOUNT);
1152 }
Ihab Awad104f8062014-07-17 11:29:35 -07001153 }
1154
Tyler Gunn84253572014-09-02 14:50:05 -07001155 public PhoneAccount readFromXml(XmlPullParser parser, int version, Context context)
Ihab Awadb78b2762014-07-25 15:16:23 -07001156 throws IOException, XmlPullParserException {
1157 if (parser.getName().equals(CLASS_PHONE_ACCOUNT)) {
1158 int outerDepth = parser.getDepth();
1159 PhoneAccountHandle accountHandle = null;
Andrew Lee7129f1c2014-09-04 11:55:07 -07001160 Uri address = null;
1161 Uri subscriptionAddress = null;
Ihab Awadb78b2762014-07-25 15:16:23 -07001162 int capabilities = 0;
Ihab Awad7e2c7f32014-11-03 09:49:45 -08001163 int iconResId = PhoneAccount.NO_RESOURCE_ID;
Ihab Awadd9f54382014-10-24 11:44:47 -07001164 String iconPackageName = null;
Ihab Awad7e2c7f32014-11-03 09:49:45 -08001165 Bitmap iconBitmap = null;
Ihab Awad07bc5ee2014-11-12 13:42:52 -08001166 int iconTint = PhoneAccount.NO_ICON_TINT;
1167 int highlightColor = PhoneAccount.NO_HIGHLIGHT_COLOR;
Ihab Awadb78b2762014-07-25 15:16:23 -07001168 String label = null;
1169 String shortDescription = null;
Tyler Gunn84253572014-09-02 14:50:05 -07001170 List<String> supportedUriSchemes = null;
Santos Cordon9c30c282015-05-13 16:28:27 -07001171 Icon icon = null;
Santos Cordonea5cb932015-05-07 16:28:38 -07001172 boolean enabled = false;
Ihab Awadb78b2762014-07-25 15:16:23 -07001173
1174 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
1175 if (parser.getName().equals(ACCOUNT_HANDLE)) {
1176 parser.nextTag();
Tyler Gunn84253572014-09-02 14:50:05 -07001177 accountHandle = sPhoneAccountHandleXml.readFromXml(parser, version,
1178 context);
Andrew Lee7129f1c2014-09-04 11:55:07 -07001179 } else if (parser.getName().equals(ADDRESS)) {
Ihab Awadb78b2762014-07-25 15:16:23 -07001180 parser.next();
Andrew Lee7129f1c2014-09-04 11:55:07 -07001181 address = Uri.parse(parser.getText());
1182 } else if (parser.getName().equals(SUBSCRIPTION_ADDRESS)) {
Ihab Awadb78b2762014-07-25 15:16:23 -07001183 parser.next();
Andrew Lee7129f1c2014-09-04 11:55:07 -07001184 String nextText = parser.getText();
1185 subscriptionAddress = nextText == null ? null : Uri.parse(nextText);
Ihab Awadb78b2762014-07-25 15:16:23 -07001186 } else if (parser.getName().equals(CAPABILITIES)) {
1187 parser.next();
1188 capabilities = Integer.parseInt(parser.getText());
1189 } else if (parser.getName().equals(ICON_RES_ID)) {
1190 parser.next();
1191 iconResId = Integer.parseInt(parser.getText());
Ihab Awadd9f54382014-10-24 11:44:47 -07001192 } else if (parser.getName().equals(ICON_PACKAGE_NAME)) {
1193 parser.next();
1194 iconPackageName = parser.getText();
1195 } else if (parser.getName().equals(ICON_BITMAP)) {
1196 parser.next();
Ihab Awad7e2c7f32014-11-03 09:49:45 -08001197 iconBitmap = readBitmap(parser);
1198 } else if (parser.getName().equals(ICON_TINT)) {
Nancy Chen06ce0622014-10-23 01:17:35 +00001199 parser.next();
Ihab Awad7e2c7f32014-11-03 09:49:45 -08001200 iconTint = Integer.parseInt(parser.getText());
1201 } else if (parser.getName().equals(HIGHLIGHT_COLOR)) {
1202 parser.next();
1203 highlightColor = Integer.parseInt(parser.getText());
Ihab Awadb78b2762014-07-25 15:16:23 -07001204 } else if (parser.getName().equals(LABEL)) {
1205 parser.next();
1206 label = parser.getText();
1207 } else if (parser.getName().equals(SHORT_DESCRIPTION)) {
1208 parser.next();
1209 shortDescription = parser.getText();
Tyler Gunn84253572014-09-02 14:50:05 -07001210 } else if (parser.getName().equals(SUPPORTED_URI_SCHEMES)) {
1211 supportedUriSchemes = readStringList(parser);
Santos Cordon9c30c282015-05-13 16:28:27 -07001212 } else if (parser.getName().equals(ICON)) {
1213 parser.next();
1214 icon = readIcon(parser);
Santos Cordonea5cb932015-05-07 16:28:38 -07001215 } else if (parser.getName().equals(ENABLED)) {
1216 parser.next();
1217 enabled = "true".equalsIgnoreCase(parser.getText());
Ihab Awadb78b2762014-07-25 15:16:23 -07001218 }
1219 }
Tyler Gunn84253572014-09-02 14:50:05 -07001220
Santos Cordona9eebe42015-06-11 14:07:44 -07001221 ComponentName pstnComponentName = new ComponentName("com.android.phone",
1222 "com.android.services.telephony.TelephonyConnectionService");
Santos Cordona82aed82015-05-26 10:43:56 -07001223 ComponentName sipComponentName = new ComponentName("com.android.phone",
1224 "com.android.services.telephony.sip.SipConnectionService");
1225
Tyler Gunn84253572014-09-02 14:50:05 -07001226 // Upgrade older phone accounts to specify the supported URI schemes.
1227 if (version < 2) {
Tyler Gunn84253572014-09-02 14:50:05 -07001228 supportedUriSchemes = new ArrayList<>();
1229
1230 // Handle the SIP connection service.
1231 // Check the system settings to see if it also should handle "tel" calls.
1232 if (accountHandle.getComponentName().equals(sipComponentName)) {
1233 boolean useSipForPstn = useSipForPstnCalls(context);
1234 supportedUriSchemes.add(PhoneAccount.SCHEME_SIP);
1235 if (useSipForPstn) {
1236 supportedUriSchemes.add(PhoneAccount.SCHEME_TEL);
1237 }
1238 } else {
1239 supportedUriSchemes.add(PhoneAccount.SCHEME_TEL);
1240 supportedUriSchemes.add(PhoneAccount.SCHEME_VOICEMAIL);
1241 }
1242 }
1243
Ihab Awad7e2c7f32014-11-03 09:49:45 -08001244 // Upgrade older phone accounts with explicit package name
1245 if (version < 5) {
1246 if (iconBitmap == null) {
1247 iconPackageName = accountHandle.getComponentName().getPackageName();
1248 }
1249 }
1250
Santos Cordona82aed82015-05-26 10:43:56 -07001251 if (version < 6) {
1252 // Always enable all SIP accounts on upgrade to version 6
1253 if (accountHandle.getComponentName().equals(sipComponentName)) {
1254 enabled = true;
1255 }
1256 }
Santos Cordona9eebe42015-06-11 14:07:44 -07001257 if (version < 7) {
1258 // Always enabled all PSTN acocunts on upgrade to version 7
1259 if (accountHandle.getComponentName().equals(pstnComponentName)) {
1260 enabled = true;
1261 }
1262 }
Roshan Pius6f752c82015-08-10 10:42:06 -07001263 if (version < 8) {
1264 // Migrate the SIP account handle ids to use SIP username instead of SIP URI.
1265 if (accountHandle.getComponentName().equals(sipComponentName)) {
1266 Uri accountUri = Uri.parse(accountHandle.getId());
1267 if (accountUri.getScheme() != null &&
1268 accountUri.getScheme().equals(PhoneAccount.SCHEME_SIP)) {
1269 accountHandle = new PhoneAccountHandle(accountHandle.getComponentName(),
1270 accountUri.getSchemeSpecificPart(),
1271 accountHandle.getUserHandle());
1272 }
1273 }
1274 }
Santos Cordona82aed82015-05-26 10:43:56 -07001275
Ihab Awad7e2c7f32014-11-03 09:49:45 -08001276 PhoneAccount.Builder builder = PhoneAccount.builder(accountHandle, label)
Andrew Lee7129f1c2014-09-04 11:55:07 -07001277 .setAddress(address)
1278 .setSubscriptionAddress(subscriptionAddress)
1279 .setCapabilities(capabilities)
Andrew Lee7129f1c2014-09-04 11:55:07 -07001280 .setShortDescription(shortDescription)
1281 .setSupportedUriSchemes(supportedUriSchemes)
Santos Cordonea5cb932015-05-07 16:28:38 -07001282 .setHighlightColor(highlightColor)
1283 .setIsEnabled(enabled);
Ihab Awad7e2c7f32014-11-03 09:49:45 -08001284
Santos Cordon9c30c282015-05-13 16:28:27 -07001285 if (icon != null) {
1286 builder.setIcon(icon);
1287 } else if (iconBitmap != null) {
1288 builder.setIcon(Icon.createWithBitmap(iconBitmap));
1289 } else if (!TextUtils.isEmpty(iconPackageName)) {
1290 builder.setIcon(Icon.createWithResource(iconPackageName, iconResId));
1291 // TODO: Need to set tint.
Ihab Awad7e2c7f32014-11-03 09:49:45 -08001292 }
1293
Ihab Awad0a4b95f2015-05-18 10:15:38 -07001294 return builder.build();
Ihab Awadb78b2762014-07-25 15:16:23 -07001295 }
1296 return null;
Ihab Awad104f8062014-07-17 11:29:35 -07001297 }
Tyler Gunn84253572014-09-02 14:50:05 -07001298
1299 /**
Santos Cordon9c30c282015-05-13 16:28:27 -07001300 * Determines if the SIP call settings specify to use SIP for all calls, including PSTN
1301 * calls.
Tyler Gunn84253572014-09-02 14:50:05 -07001302 *
1303 * @param context The context.
1304 * @return {@code True} if SIP should be used for all calls.
1305 */
1306 private boolean useSipForPstnCalls(Context context) {
1307 String option = Settings.System.getString(context.getContentResolver(),
1308 Settings.System.SIP_CALL_OPTIONS);
1309 option = (option != null) ? option : Settings.System.SIP_ADDRESS_ONLY;
1310 return option.equals(Settings.System.SIP_ALWAYS);
1311 }
Ihab Awad104f8062014-07-17 11:29:35 -07001312 };
1313
Ihab Awadb78b2762014-07-25 15:16:23 -07001314 @VisibleForTesting
1315 public static final XmlSerialization<PhoneAccountHandle> sPhoneAccountHandleXml =
1316 new XmlSerialization<PhoneAccountHandle>() {
1317 private static final String CLASS_PHONE_ACCOUNT_HANDLE = "phone_account_handle";
Ihab Awad104f8062014-07-17 11:29:35 -07001318 private static final String COMPONENT_NAME = "component_name";
1319 private static final String ID = "id";
Evan Charlton105d9772014-11-25 14:08:53 -08001320 private static final String USER_SERIAL_NUMBER = "user_serial_number";
Ihab Awad104f8062014-07-17 11:29:35 -07001321
1322 @Override
Evan Charlton105d9772014-11-25 14:08:53 -08001323 public void writeToXml(PhoneAccountHandle o, XmlSerializer serializer, Context context)
Ihab Awadb78b2762014-07-25 15:16:23 -07001324 throws IOException {
Ihab Awad26923222014-07-30 10:54:35 -07001325 if (o != null) {
1326 serializer.startTag(null, CLASS_PHONE_ACCOUNT_HANDLE);
Ihab Awadb78b2762014-07-25 15:16:23 -07001327
Ihab Awad26923222014-07-30 10:54:35 -07001328 if (o.getComponentName() != null) {
Ihab Awadd9f54382014-10-24 11:44:47 -07001329 writeTextIfNonNull(
Ihab Awad26923222014-07-30 10:54:35 -07001330 COMPONENT_NAME, o.getComponentName().flattenToString(), serializer);
1331 }
Ihab Awadb78b2762014-07-25 15:16:23 -07001332
Ihab Awadd9f54382014-10-24 11:44:47 -07001333 writeTextIfNonNull(ID, o.getId(), serializer);
Ihab Awadb78b2762014-07-25 15:16:23 -07001334
Evan Charlton105d9772014-11-25 14:08:53 -08001335 if (o.getUserHandle() != null && context != null) {
1336 UserManager userManager = UserManager.get(context);
1337 writeLong(USER_SERIAL_NUMBER,
1338 userManager.getSerialNumberForUser(o.getUserHandle()), serializer);
1339 }
1340
Ihab Awad26923222014-07-30 10:54:35 -07001341 serializer.endTag(null, CLASS_PHONE_ACCOUNT_HANDLE);
1342 }
Ihab Awad104f8062014-07-17 11:29:35 -07001343 }
1344
1345 @Override
Tyler Gunn84253572014-09-02 14:50:05 -07001346 public PhoneAccountHandle readFromXml(XmlPullParser parser, int version, Context context)
Ihab Awadb78b2762014-07-25 15:16:23 -07001347 throws IOException, XmlPullParserException {
1348 if (parser.getName().equals(CLASS_PHONE_ACCOUNT_HANDLE)) {
1349 String componentNameString = null;
1350 String idString = null;
Evan Charlton105d9772014-11-25 14:08:53 -08001351 String userSerialNumberString = null;
Ihab Awadb78b2762014-07-25 15:16:23 -07001352 int outerDepth = parser.getDepth();
Evan Charlton105d9772014-11-25 14:08:53 -08001353
1354 UserManager userManager = UserManager.get(context);
1355
Ihab Awadb78b2762014-07-25 15:16:23 -07001356 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
1357 if (parser.getName().equals(COMPONENT_NAME)) {
1358 parser.next();
1359 componentNameString = parser.getText();
1360 } else if (parser.getName().equals(ID)) {
1361 parser.next();
1362 idString = parser.getText();
Evan Charlton105d9772014-11-25 14:08:53 -08001363 } else if (parser.getName().equals(USER_SERIAL_NUMBER)) {
1364 parser.next();
1365 userSerialNumberString = parser.getText();
Ihab Awadb78b2762014-07-25 15:16:23 -07001366 }
1367 }
Ihab Awad26923222014-07-30 10:54:35 -07001368 if (componentNameString != null) {
Evan Charlton105d9772014-11-25 14:08:53 -08001369 UserHandle userHandle = null;
1370 if (userSerialNumberString != null) {
1371 try {
1372 long serialNumber = Long.parseLong(userSerialNumberString);
1373 userHandle = userManager.getUserForSerialNumber(serialNumber);
1374 } catch (NumberFormatException e) {
1375 Log.e(this, e, "Could not parse UserHandle " + userSerialNumberString);
1376 }
1377 }
Ihab Awadb78b2762014-07-25 15:16:23 -07001378 return new PhoneAccountHandle(
1379 ComponentName.unflattenFromString(componentNameString),
Evan Charlton105d9772014-11-25 14:08:53 -08001380 idString,
1381 userHandle);
Ihab Awadb78b2762014-07-25 15:16:23 -07001382 }
1383 }
1384 return null;
Ihab Awad104f8062014-07-17 11:29:35 -07001385 }
1386 };
Santos Cordon176ae282014-07-14 02:02:14 -07001387}