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