• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.internal.telephony.domainselection;
18 
19 import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
20 
21 import static com.android.internal.telephony.RIL.RADIO_HAL_VERSION_2_1;
22 
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.content.ComponentName;
26 import android.content.Context;
27 import android.os.SystemProperties;
28 import android.telephony.DomainSelectionService;
29 import android.text.TextUtils;
30 import android.util.IndentingPrintWriter;
31 import android.util.LocalLog;
32 import android.util.Log;
33 
34 import com.android.internal.annotations.VisibleForTesting;
35 import com.android.internal.telephony.Phone;
36 import com.android.internal.telephony.PhoneFactory;
37 import com.android.internal.telephony.flags.Flags;
38 import com.android.internal.telephony.util.TelephonyUtils;
39 
40 import java.io.FileDescriptor;
41 import java.io.PrintWriter;
42 
43 /**
44  * This class is an entry point to provide whether the AOSP domain selection is supported or not,
45  * and bind the {@link DomainSelectionController} with the given {@link DomainSelectionService} to
46  * provide a specific {@link DomainSelectionConnection} object for communicating with each domain
47  * selector.
48  */
49 public class DomainSelectionResolver {
50     @VisibleForTesting
51     protected static final String PACKAGE_NAME_NONE = "none";
52     private static final String TAG = DomainSelectionResolver.class.getSimpleName();
53     private static final boolean DBG = TelephonyUtils.IS_DEBUGGABLE;
54     /** For test purpose only with userdebug release */
55     private static final String PROP_DISABLE_DOMAIN_SELECTION =
56             "telephony.test.disable_domain_selection";
57     private static DomainSelectionResolver sInstance = null;
58 
59     /**
60      * Creates the DomainSelectionResolver singleton instance.
61      *
62      * @param context The context of the application.
63      * @param flattenedComponentName A flattened component name for the domain selection service
64      *                               to be bound to the domain selection controller.
65      */
make(Context context, String flattenedComponentName)66     public static void make(Context context, String flattenedComponentName) {
67         Log.i(TAG, "make useOem=" + Flags.useOemDomainSelectionService());
68         if (sInstance == null) {
69             sInstance = new DomainSelectionResolver(context, flattenedComponentName);
70         }
71     }
72 
73     /**
74      * Returns the singleton instance of DomainSelectionResolver.
75      *
76      * @return A {@link DomainSelectionResolver} instance.
77      */
getInstance()78     public static DomainSelectionResolver getInstance() {
79         if (sInstance == null) {
80             throw new IllegalStateException("DomainSelectionResolver is not ready!");
81         }
82         return sInstance;
83     }
84 
85     /**
86      * Sets a {@link DomainSelectionResolver} for injecting mock DomainSelectionResolver.
87      *
88      * @param resolver A {@link DomainSelectionResolver} instance to test.
89      */
90     @VisibleForTesting
setDomainSelectionResolver(DomainSelectionResolver resolver)91     public static void setDomainSelectionResolver(DomainSelectionResolver resolver) {
92         sInstance = resolver;
93     }
94 
95     /**
96      * Testing interface for injecting mock DomainSelectionController.
97      */
98     @VisibleForTesting
99     public interface DomainSelectionControllerFactory {
100         /**
101          * Returns a {@link DomainSelectionController} created using the specified context.
102          */
create(@onNull Context context)103         DomainSelectionController create(@NonNull Context context);
104     }
105 
106     private DomainSelectionControllerFactory mDomainSelectionControllerFactory =
107             new DomainSelectionControllerFactory() {
108                 @Override
109                 public DomainSelectionController create(@NonNull Context context) {
110                     return new DomainSelectionController(context);
111                 }
112             };
113 
114     // Persistent Logging
115     private final LocalLog mEventLog = new LocalLog(10);
116     private final Context mContext;
117     // Stores the default component name to bind the domain selection service so that
118     // the test can override this component name with their own domain selection service.
119     private final ComponentName mDefaultComponentName;
120     // DomainSelectionController, which are bound to DomainSelectionService.
121     private DomainSelectionController mController;
122 
DomainSelectionResolver(Context context, String flattenedComponentName)123     public DomainSelectionResolver(Context context, String flattenedComponentName) {
124         mContext = context;
125         flattenedComponentName = (flattenedComponentName == null) ? "" : flattenedComponentName;
126         mDefaultComponentName = ComponentName.unflattenFromString(flattenedComponentName);
127         logi("DomainSelectionResolver created: componentName=[" + flattenedComponentName + "]");
128     }
129 
130     /**
131      * Checks if the device supports the domain selection service to route the call / SMS /
132      * supplementary services to the appropriate domain.
133      * This checks the device-config and Radio HAL version for supporting the domain selection.
134      * The domain selection requires the Radio HAL version greater than or equal to 2.1.
135      *
136      * @return {@code true} if the domain selection is supported on the device,
137      *         {@code false} otherwise.
138      */
isDomainSelectionSupported()139     public boolean isDomainSelectionSupported() {
140         if (DBG && SystemProperties.getBoolean(PROP_DISABLE_DOMAIN_SELECTION, false)) {
141             logi("Disabled for test");
142             return false;
143         }
144         return mDefaultComponentName != null && PhoneFactory.getDefaultPhone()
145                 .getHalVersion(HAL_SERVICE_NETWORK).greaterOrEqual(RADIO_HAL_VERSION_2_1);
146     }
147 
148     /**
149      * Returns a {@link DomainSelectionConnection} instance.
150      *
151      * @param phone The Phone instance for witch this request is.
152      * @param selectorType Indicates the selector type requested.
153      * @param isEmergency Indicates whether this is for emergency service.
154      * @throws IllegalStateException If the {@link DomainSelectionController} is not created
155      *         because {@link #initialize} method is not called even if the domain selection is
156      *         supported.
157      * @return A {@link DomainSelectionConnection} instance if the device supports
158      *         AOSP domain selection and IMS is available or {@code null} otherwise.
159      */
getDomainSelectionConnection(Phone phone, @DomainSelectionService.SelectorType int selectorType, boolean isEmergency)160     public @Nullable DomainSelectionConnection getDomainSelectionConnection(Phone phone,
161             @DomainSelectionService.SelectorType int selectorType, boolean isEmergency) {
162         if (mController == null) {
163             // If the caller calls this method without checking whether the domain selection
164             // is supported or not, this exception will be thrown.
165             throw new IllegalStateException("DomainSelection is not supported!");
166         }
167 
168         if (phone == null || phone.getImsPhone() == null
169                 || (!(isEmergency && selectorType == DomainSelectionService.SELECTOR_TYPE_CALLING)
170                         && !phone.isImsAvailable())) {
171             // In case of emergency calls, to recover the temporary failure in IMS service
172             // connection, DomainSelection shall be started even when IMS isn't available.
173             // DomainSelector will keep finding next available transport.
174             // For other telephony services, if the binder of ImsService is not available,
175             // CS domain will be used.
176             return null;
177         }
178 
179         return mController.getDomainSelectionConnection(phone, selectorType, isEmergency);
180     }
181 
182     /** Sets a factory interface for creating {@link DomainSelectionController} instance. */
183     @VisibleForTesting
setDomainSelectionControllerFactory(DomainSelectionControllerFactory factory)184     public void setDomainSelectionControllerFactory(DomainSelectionControllerFactory factory) {
185         mDomainSelectionControllerFactory = factory;
186     }
187 
188     /**
189      * Creates the {@link DomainSelectionController} and requests the domain selection controller
190      * to bind to the {@link DomainSelectionService} with the component name.
191      */
initialize()192     public void initialize() {
193         logi("Initialize");
194         mController = mDomainSelectionControllerFactory.create(mContext);
195         if (mDefaultComponentName != null) {
196             mController.bind(mDefaultComponentName);
197         } else {
198             logi("No component name specified for domain selection service.");
199         }
200     }
201 
202     /**
203      * Sets the component name of domain selection service to be bound.
204      *
205      * NOTE: This should only be used for testing.
206      *
207      * @return {@code true} if the requested operation is successfully done,
208      *         {@code false} otherwise.
209      */
setDomainSelectionServiceOverride(@onNull ComponentName componentName)210     public boolean setDomainSelectionServiceOverride(@NonNull ComponentName componentName) {
211         if (mController == null) {
212             logd("Controller is not initialized.");
213             return false;
214         }
215         logi("setDomainSelectionServiceOverride: " + componentName);
216         if (TextUtils.isEmpty(componentName.getPackageName())
217                 || TextUtils.equals(PACKAGE_NAME_NONE, componentName.getPackageName())) {
218             // Unbind the active service connection to the domain selection service.
219             mController.unbind();
220             return true;
221         }
222         // Override the domain selection service with the given component name.
223         return mController.bind(componentName);
224     }
225 
226     /**
227      * Clears the overridden domain selection service and restores the domain selection service
228      * with the default component.
229      *
230      * NOTE: This should only be used for testing.
231      *
232      * @return {@code true} if the requested operation is successfully done,
233      *         {@code false} otherwise.
234      */
clearDomainSelectionServiceOverride()235     public boolean clearDomainSelectionServiceOverride() {
236         if (mController == null) {
237             logd("Controller is not initialized.");
238             return false;
239         }
240         logi("clearDomainSelectionServiceOverride");
241         mController.unbind();
242         return mController.bind(mDefaultComponentName);
243     }
244 
245     /**
246      * Dumps this instance into a readable format for dumpsys usage.
247      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)248     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
249         IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
250         ipw.println("Resolver:");
251         ipw.increaseIndent();
252         ipw.println("Event Log:");
253         ipw.increaseIndent();
254         mEventLog.dump(ipw);
255         ipw.decreaseIndent();
256         ipw.decreaseIndent();
257 
258         ipw.println("Controller:");
259         ipw.increaseIndent();
260         DomainSelectionController controller = mController;
261         if (controller == null) {
262             ipw.println("no active controller");
263         } else {
264             controller.dump(ipw);
265         }
266         ipw.decreaseIndent();
267     }
268 
logd(String s)269     private void logd(String s) {
270         Log.d(TAG, s);
271     }
272 
logi(String s)273     private void logi(String s) {
274         Log.i(TAG, s);
275         mEventLog.log(s);
276     }
277 }
278