• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 
17 package com.android.ims.rcs.uce.presence.publish;
18 
19 import android.content.Context;
20 import android.net.Uri;
21 import android.telecom.PhoneAccount;
22 import android.telephony.PhoneNumberUtils;
23 import android.telephony.TelephonyManager;
24 import android.telephony.ims.feature.RcsFeature.RcsImsCapabilities;
25 import android.telephony.ims.feature.RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag;
26 import android.text.TextUtils;
27 import android.util.Log;
28 
29 import com.android.i18n.phonenumbers.NumberParseException;
30 import com.android.i18n.phonenumbers.PhoneNumberUtil;
31 import com.android.i18n.phonenumbers.Phonenumber;
32 import com.android.ims.rcs.uce.util.UceUtils;
33 
34 import java.util.Arrays;
35 
36 /**
37  * The util class of publishing device's capabilities.
38  */
39 public class PublishUtils {
40     private static final String LOG_TAG = UceUtils.getLogPrefix() + "PublishUtils";
41 
42     private static final String SCHEME_SIP = "sip";
43     private static final String SCHEME_TEL = "tel";
44     private static final String DOMAIN_SEPARATOR = "@";
45 
46     /**
47      * @return the contact URI of this device for either a PRESENCE or OPTIONS capabilities request.
48      * We will first try to use the IMS service associated URIs from the p-associated-uri header
49      * in the IMS registration response. If this is not available, we will fall back to using the
50      * SIM card information to generate the URI.
51      */
getDeviceContactUri(Context context, int subId, DeviceCapabilityInfo deviceCap, boolean isForPresence)52     public static Uri getDeviceContactUri(Context context, int subId,
53             DeviceCapabilityInfo deviceCap, boolean isForPresence) {
54         boolean preferTelUri = false;
55         if (isForPresence) {
56             preferTelUri = UceUtils.isTelUriForPidfXmlEnabled(context, subId);
57         }
58         // Get the uri from the IMS p-associated-uri header which is provided by the IMS service.
59         Uri contactUri = deviceCap.getImsAssociatedUri(preferTelUri);
60         if (contactUri != null) {
61             Uri convertedUri = preferTelUri ? getConvertedTelUri(context, contactUri) : contactUri;
62             Log.d(LOG_TAG, "getDeviceContactUri: returning "
63                     + (contactUri.equals(convertedUri) ? "found" : "converted")
64                     + " ims associated uri");
65             return contactUri;
66         }
67 
68         // No IMS service provided URIs, so generate the contact uri from ISIM.
69         TelephonyManager telephonyManager = getTelephonyManager(context, subId);
70         if (telephonyManager == null) {
71             Log.w(LOG_TAG, "getDeviceContactUri: TelephonyManager is null");
72             return null;
73         }
74         contactUri = getContactUriFromIsim(telephonyManager);
75         if (contactUri != null) {
76             Log.d(LOG_TAG, "getDeviceContactUri: impu");
77             if (preferTelUri) {
78                 return getConvertedTelUri(context, contactUri);
79             } else {
80                 return contactUri;
81             }
82         } else {
83             Log.d(LOG_TAG, "getDeviceContactUri: line number");
84             if (preferTelUri) {
85                 return getConvertedTelUri(context, getContactUriFromLine1Number(telephonyManager));
86             } else {
87                 return getContactUriFromLine1Number(telephonyManager);
88             }
89         }
90     }
91 
92     /**
93      * Find all instances of sip/sips/tel URIs containing PII and replace them.
94      * <p>
95      * This is used for removing PII in logging.
96      * @param source The source string to remove the phone numbers from.
97      * @return A version of the given string with SIP URIs removed.
98      */
removeNumbersFromUris(String source)99     public static String removeNumbersFromUris(String source) {
100         // Replace only the number portion in the sip/sips/tel URI
101         return source.replaceAll("(?:sips?|tel):(\\+?[\\d\\-]+)", "[removed]");
102     }
103 
getContactUriFromIsim(TelephonyManager telephonyManager)104     private static Uri getContactUriFromIsim(TelephonyManager telephonyManager) {
105         // Get the home network domain and the array of the public user identities
106         String domain = telephonyManager.getIsimDomain();
107         String[] impus = telephonyManager.getIsimImpu();
108 
109         if (TextUtils.isEmpty(domain) || impus == null) {
110             Log.d(LOG_TAG, "getContactUriFromIsim: domain is null=" + TextUtils.isEmpty(domain));
111             Log.d(LOG_TAG, "getContactUriFromIsim: impu is null=" +
112                     ((impus == null || impus.length == 0) ? "true" : "false"));
113             return null;
114         }
115 
116         for (String impu : impus) {
117             if (TextUtils.isEmpty(impu)) continue;
118             Uri impuUri = Uri.parse(impu);
119             String scheme = impuUri.getScheme();
120             String schemeSpecificPart = impuUri.getSchemeSpecificPart();
121             if (SCHEME_SIP.equals(scheme) && !TextUtils.isEmpty(schemeSpecificPart) &&
122                     schemeSpecificPart.endsWith(domain)) {
123                 return impuUri;
124             }
125         }
126         Log.d(LOG_TAG, "getContactUriFromIsim: there is no impu matching the domain");
127         return null;
128     }
129 
getContactUriFromLine1Number(TelephonyManager telephonyManager)130     private static Uri getContactUriFromLine1Number(TelephonyManager telephonyManager) {
131         String phoneNumber = formatPhoneNumber(telephonyManager.getLine1Number());
132         if (TextUtils.isEmpty(phoneNumber)) {
133             Log.w(LOG_TAG, "Cannot get the phone number");
134             return null;
135         }
136 
137         String domain = telephonyManager.getIsimDomain();
138         if (!TextUtils.isEmpty(domain)) {
139             return Uri.fromParts(SCHEME_SIP, phoneNumber + DOMAIN_SEPARATOR + domain, null);
140         } else {
141             return Uri.fromParts(SCHEME_TEL, phoneNumber, null);
142         }
143     }
144 
formatPhoneNumber(final String phoneNumber)145     private static String formatPhoneNumber(final String phoneNumber) {
146         if (TextUtils.isEmpty(phoneNumber)) {
147             Log.w(LOG_TAG, "formatPhoneNumber: phone number is empty");
148             return null;
149         }
150         String number = PhoneNumberUtils.stripSeparators(phoneNumber);
151         return PhoneNumberUtils.normalizeNumber(number);
152     }
153 
getTelephonyManager(Context context, int subId)154     private static TelephonyManager getTelephonyManager(Context context, int subId) {
155         TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
156         if (telephonyManager == null) {
157             return null;
158         } else {
159             return telephonyManager.createForSubscriptionId(subId);
160         }
161     }
162 
163     /**
164      * @return a TEL URI version of the contact URI if given a SIP URI. If given a TEL URI, this
165      * method will return the same value given.
166      */
getConvertedTelUri(Context context, Uri contactUri)167     private static Uri getConvertedTelUri(Context context, Uri contactUri) {
168         if (contactUri == null) {
169             return null;
170         }
171         if (contactUri.getScheme().equalsIgnoreCase(SCHEME_SIP)) {
172             TelephonyManager manager = context.getSystemService(TelephonyManager.class);
173             if (manager.getIsimDomain() == null) {
174                 return contactUri;
175             }
176 
177             String numbers = contactUri.getSchemeSpecificPart();
178             String[] numberParts = numbers.split("[@;:]");
179             String number = numberParts[0];
180 
181             String simCountryIso = manager.getSimCountryIso();
182             if (!TextUtils.isEmpty(simCountryIso)) {
183                 simCountryIso = simCountryIso.toUpperCase();
184                 PhoneNumberUtil util = PhoneNumberUtil.getInstance();
185                 try {
186                     Phonenumber.PhoneNumber phoneNumber = util.parse(number, simCountryIso);
187                     number = util.format(phoneNumber, PhoneNumberUtil.PhoneNumberFormat.E164);
188                     String telUri = SCHEME_TEL + ":" + number;
189                     contactUri = Uri.parse(telUri);
190                 } catch (NumberParseException e) {
191                     Log.w(LOG_TAG, "formatNumber: could not format " + number + ", error: " + e);
192                 }
193             }
194         }
195         return contactUri;
196     }
197 
getCapabilityType(Context context, int subId)198     static @RcsImsCapabilityFlag int getCapabilityType(Context context, int subId) {
199         boolean isPresenceSupported = UceUtils.isPresenceSupported(context, subId);
200         boolean isSipOptionsSupported = UceUtils.isSipOptionsSupported(context, subId);
201         if (isPresenceSupported) {
202             return RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE;
203         } else if (isSipOptionsSupported) {
204             return RcsImsCapabilities.CAPABILITY_TYPE_OPTIONS_UCE;
205         } else {
206             // Return NONE when neither OPTIONS nor PRESENCE is supported.
207             return RcsImsCapabilities.CAPABILITY_TYPE_NONE;
208         }
209     }
210 }
211