1 /*
2  * Copyright (C) 2018 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.server.telecom.callredirection;
18 
19 import android.Manifest;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.pm.ResolveInfo;
24 import android.net.Uri;
25 import android.os.PersistableBundle;
26 import android.os.UserHandle;
27 import android.telecom.CallRedirectionService;
28 import android.telecom.GatewayInfo;
29 import android.telecom.Log;
30 import android.telecom.PhoneAccountHandle;
31 import android.telephony.CarrierConfigManager;
32 import android.telephony.PhoneNumberUtils;
33 import android.telephony.TelephonyManager;
34 import android.text.TextUtils;
35 import com.android.internal.annotations.VisibleForTesting;
36 import com.android.server.telecom.CallsManager;
37 import com.android.server.telecom.PhoneAccountRegistrar;
38 
39 import java.util.List;
40 
41 public class CallRedirectionProcessorHelper {
42 
43     private final Context mContext;
44     private final CallsManager mCallsManager;
45     private final PhoneAccountRegistrar mPhoneAccountRegistrar;
46 
CallRedirectionProcessorHelper( Context context, CallsManager callsManager, PhoneAccountRegistrar phoneAccountRegistrar)47     public CallRedirectionProcessorHelper(
48             Context context,
49             CallsManager callsManager,
50             PhoneAccountRegistrar phoneAccountRegistrar) {
51         mContext = context;
52         mCallsManager = callsManager;
53         mPhoneAccountRegistrar = phoneAccountRegistrar;
54     }
55 
56     @VisibleForTesting
getUserDefinedCallRedirectionService(UserHandle userHandle)57     public ComponentName getUserDefinedCallRedirectionService(UserHandle userHandle) {
58         String packageName = mCallsManager.getRoleManagerAdapter().getDefaultCallRedirectionApp(
59                 userHandle);
60         if (TextUtils.isEmpty(packageName)) {
61             Log.i(this, "PackageName is empty. Not performing user-defined call redirection.");
62             return null;
63         }
64         Intent intent = new Intent(CallRedirectionService.SERVICE_INTERFACE)
65                 .setPackage(packageName);
66         return getComponentName(intent, CallRedirectionProcessor.SERVICE_TYPE_USER_DEFINED);
67     }
68 
69     @VisibleForTesting
getCarrierCallRedirectionService( PhoneAccountHandle targetPhoneAccountHandle)70     public ComponentName getCarrierCallRedirectionService(
71             PhoneAccountHandle targetPhoneAccountHandle) {
72         CarrierConfigManager configManager = (CarrierConfigManager)
73                 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
74         if (configManager == null) {
75             Log.i(this, "Cannot get CarrierConfigManager.");
76             return null;
77         }
78         PersistableBundle pb = configManager.getConfigForSubId(mPhoneAccountRegistrar
79                 .getSubscriptionIdForPhoneAccount(targetPhoneAccountHandle));
80         if (pb == null) {
81             Log.i(this, "Cannot get PersistableBundle.");
82             return null;
83         }
84         String componentNameString = pb.getString(
85                 CarrierConfigManager.KEY_CALL_REDIRECTION_SERVICE_COMPONENT_NAME_STRING);
86         if (componentNameString == null) {
87             Log.i(this, "Cannot get carrier componentNameString.");
88             return null;
89         }
90         ComponentName componentName = ComponentName.unflattenFromString(componentNameString);
91         if (componentName == null) {
92             Log.w(this, "ComponentName is null from string: " + componentNameString);
93             return null;
94         }
95         Intent intent = new Intent(CallRedirectionService.SERVICE_INTERFACE);
96         intent.setComponent(componentName);
97         return getComponentName(intent, CallRedirectionProcessor.SERVICE_TYPE_CARRIER);
98     }
99 
getComponentName(Intent intent, String serviceType)100     protected ComponentName getComponentName(Intent intent, String serviceType) {
101         List<ResolveInfo> entries = mContext.getPackageManager().queryIntentServicesAsUser(
102                 intent, 0, mCallsManager.getCurrentUserHandle().getIdentifier());
103         if (entries.isEmpty()) {
104             Log.i(this, "There are no " + serviceType + " call redirection services installed" +
105                     " on this device.");
106             return null;
107         } else if (entries.size() != 1) {
108             Log.i(this, "There are multiple " + serviceType + " call redirection services" +
109                     " installed on this device.");
110             return null;
111         }
112         ResolveInfo entry = entries.get(0);
113         if (entry.serviceInfo == null) {
114             Log.w(this, "The " + serviceType + " call redirection service has invalid" +
115                     " service info");
116             return null;
117         }
118         if (entry.serviceInfo.permission == null || !entry.serviceInfo.permission.equals(
119                 Manifest.permission.BIND_CALL_REDIRECTION_SERVICE)) {
120             Log.w(this, "CallRedirectionService must require BIND_CALL_REDIRECTION_SERVICE"
121                     + " permission: " + entry.serviceInfo.packageName);
122             return null;
123         }
124         return new ComponentName(entry.serviceInfo.packageName, entry.serviceInfo.name);
125     }
126 
127     /**
128      * Format Number to E164, and remove post dial digits.
129      */
formatNumberForRedirection(Uri handle)130     protected Uri formatNumberForRedirection(Uri handle) {
131         return removePostDialDigits(formatNumberToE164(handle));
132     }
133 
134     /**
135      * Extras the post dial digits from a given handle.
136      * @param handle The handle
137      * @return The post dial digits.
138      */
getPostDialDigits(Uri handle)139     public String getPostDialDigits(Uri handle) {
140         if (handle == null) {
141             return "";
142         }
143         return PhoneNumberUtils.extractPostDialPortion(handle.getSchemeSpecificPart());
144     }
145 
146     @VisibleForTesting
formatNumberToE164(Uri handle)147     public Uri formatNumberToE164(Uri handle) {
148         String number = handle.getSchemeSpecificPart();
149 
150         // Format number to E164
151         TelephonyManager tm = (TelephonyManager) mContext.getSystemService(
152                 Context.TELEPHONY_SERVICE);
153         Log.i(this, "formatNumberToE164, original number: " + Log.pii(number));
154         String networkCountryIso;
155         try {
156             number = PhoneNumberUtils.formatNumberToE164(number, tm.getNetworkCountryIso());
157             Log.i(this, "formatNumberToE164, formatted E164 number: " + Log.pii(number));
158         } catch (UnsupportedOperationException ignored) {
159             // Note: Yes, this could default back to the system locale, however redirection when
160             // there is no telephony is NOT expected.  Hence in reality we shouldn't really hit this
161             // code path in practice; this is a "just in case" to ensure we don't crash.
162             Log.w(this, "formatNumberToE164: no telephony; use original format");
163             number = null;
164         }
165         // if there is a problem with parsing the phone number, formatNumberToE164 will return null;
166         // and should just use the original number in that case.
167         if (number == null) {
168             return handle;
169         } else {
170             return Uri.fromParts(handle.getScheme(), number, null);
171         }
172     }
173 
removePostDialDigits(Uri handle)174     protected Uri removePostDialDigits(Uri handle) {
175         String number = handle.getSchemeSpecificPart();
176 
177         // Extract the post dial portion
178         number = PhoneNumberUtils.extractNetworkPortion(number);
179         Log.i(this, "removePostDialDigits, number after being extracted post dial digits: "
180                 + Log.pii(number));
181         // if there is a problem with parsing the phone number, removePostDialDigits will return
182         // null; and should just use the original number in that case.
183         if (number == null) {
184             return handle;
185         } else {
186             return Uri.fromParts(handle.getScheme(), number, null);
187         }
188     }
189 
getGatewayInfoFromGatewayUri( String gatewayPackageName, Uri gatewayUri, Uri destinationUri, String postdialDigits)190     public static GatewayInfo getGatewayInfoFromGatewayUri(
191             String gatewayPackageName, Uri gatewayUri, Uri destinationUri, String postdialDigits) {
192         if (!TextUtils.isEmpty(gatewayPackageName) && gatewayUri != null) {
193             Uri gatewayWithPostdial = gatewayUri;
194             if (gatewayUri != null && !TextUtils.isEmpty(postdialDigits)) {
195                 gatewayWithPostdial = new Uri.Builder()
196                         .scheme(gatewayUri.getScheme())
197                         .encodedOpaquePart(gatewayUri.getSchemeSpecificPart() + postdialDigits)
198                         .build();
199             }
200             return new GatewayInfo(gatewayPackageName, gatewayWithPostdial, destinationUri);
201         }
202         return null;
203     }
204 }
205