• 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.DomainSelectionService.SELECTOR_TYPE_CALLING;
20 import static android.telephony.DomainSelectionService.SELECTOR_TYPE_SMS;
21 
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.content.Context;
25 import android.os.AsyncResult;
26 import android.os.Handler;
27 import android.os.HandlerThread;
28 import android.os.Looper;
29 import android.os.Message;
30 import android.telephony.BarringInfo;
31 import android.telephony.DomainSelectionService;
32 import android.telephony.ServiceState;
33 import android.telephony.TelephonyManager;
34 import android.telephony.TransportSelectorCallback;
35 import android.util.LocalLog;
36 import android.util.Log;
37 
38 import com.android.internal.annotations.VisibleForTesting;
39 import com.android.internal.telephony.Phone;
40 import com.android.internal.telephony.util.TelephonyUtils;
41 
42 import java.io.PrintWriter;
43 import java.util.ArrayList;
44 import java.util.concurrent.Executor;
45 
46 /**
47  * Manages the connection to {@link DomainSelectionService}.
48  */
49 public class DomainSelectionController {
50     private static final String TAG = "DomainSelectionController";
51     private static final boolean DBG = TelephonyUtils.IS_DEBUGGABLE;
52 
53     private static final int EVENT_SERVICE_STATE_CHANGED = 1;
54     private static final int EVENT_BARRING_INFO_CHANGED = 2;
55 
56     private final HandlerThread mHandlerThread =
57             new HandlerThread("DomainSelectionControllerHandler");
58 
59     private final DomainSelectionService mDomainSelectionService;
60     private final Handler mHandler;
61     // Only added or removed, never accessed on purpose.
62     private final LocalLog mLocalLog = new LocalLog(30);
63 
64     protected final Object mLock = new Object();
65     protected final Context mContext;
66 
67     protected final int[] mConnectionCounts;
68     private final ArrayList<DomainSelectionConnection> mConnections = new ArrayList<>();
69 
70     private final class DomainSelectionControllerHandler extends Handler {
DomainSelectionControllerHandler(Looper looper)71         DomainSelectionControllerHandler(Looper looper) {
72             super(looper);
73         }
74 
75         @Override
handleMessage(Message msg)76         public void handleMessage(Message msg) {
77             AsyncResult ar;
78             switch (msg.what) {
79                 case EVENT_SERVICE_STATE_CHANGED:
80                     ar = (AsyncResult) msg.obj;
81                     updateServiceState((Phone) ar.userObj, (ServiceState) ar.result);
82                     break;
83                 case EVENT_BARRING_INFO_CHANGED:
84                     ar = (AsyncResult) msg.obj;
85                     updateBarringInfo((Phone) ar.userObj, (BarringInfo) ar.result);
86                     break;
87                 default:
88                     loge("unexpected event=" + msg.what);
89                     break;
90             }
91         }
92     }
93 
94     /**
95      * Creates an instance.
96      *
97      * @param context Context object from hosting application.
98      * @param service The {@link DomainSelectionService} instance.
99      */
DomainSelectionController(@onNull Context context, @NonNull DomainSelectionService service)100     public DomainSelectionController(@NonNull Context context,
101             @NonNull DomainSelectionService service) {
102         this(context, service, null);
103     }
104 
105     /**
106      * Creates an instance.
107      *
108      * @param context Context object from hosting application.
109      * @param service The {@link DomainSelectionService} instance.
110      * @param looper Handles event messages.
111      */
112     @VisibleForTesting
DomainSelectionController(@onNull Context context, @NonNull DomainSelectionService service, @Nullable Looper looper)113     public DomainSelectionController(@NonNull Context context,
114             @NonNull DomainSelectionService service, @Nullable Looper looper) {
115         mContext = context;
116         mDomainSelectionService = service;
117 
118         if (looper == null) {
119             mHandlerThread.start();
120             looper = mHandlerThread.getLooper();
121         }
122         mHandler = new DomainSelectionControllerHandler(looper);
123 
124         int numPhones = TelephonyManager.getDefault().getActiveModemCount();
125         mConnectionCounts = new int[numPhones];
126         for (int i = 0; i < numPhones; i++) {
127             mConnectionCounts[i] = 0;
128         }
129     }
130 
131     /**
132      * Returns a {@link DomainSelectionConnection} instance.
133      *
134      * @param phone Indicates who requests the service.
135      * @param selectorType Indicates the selector type requested.
136      * @param isEmergency Indicates whether this is for emergency service.
137      * @return A {@link DomainSelectiionConnection} instance for the requested service.
138      *         Returns {@code null} if the requested service is not supported.
139      */
getDomainSelectionConnection( @onNull Phone phone, @DomainSelectionService.SelectorType int selectorType, boolean isEmergency)140     public @Nullable DomainSelectionConnection getDomainSelectionConnection(
141             @NonNull Phone phone,
142             @DomainSelectionService.SelectorType int selectorType,
143             boolean isEmergency) {
144         DomainSelectionConnection c = null;
145 
146         if (selectorType == SELECTOR_TYPE_CALLING) {
147             if (isEmergency) {
148                 c = new EmergencyCallDomainSelectionConnection(phone, this);
149             } else {
150                 c = new NormalCallDomainSelectionConnection(phone, this);
151             }
152         } else if (selectorType == SELECTOR_TYPE_SMS) {
153             if (isEmergency) {
154                 c = new EmergencySmsDomainSelectionConnection(phone, this);
155             } else {
156                 c = new SmsDomainSelectionConnection(phone, this);
157             }
158         }
159 
160         addConnection(c);
161         return c;
162     }
163 
addConnection(@ullable DomainSelectionConnection c)164     private void addConnection(@Nullable DomainSelectionConnection c) {
165         if (c == null) return;
166         mConnections.add(c);
167         registerForStateChange(c);
168     }
169 
170     /**
171      * Releases resources for this connection.
172      */
removeConnection(@ullable DomainSelectionConnection c)173     public void removeConnection(@Nullable DomainSelectionConnection c) {
174         if (c == null) return;
175         mConnections.remove(c);
176         unregisterForStateChange(c);
177     }
178 
179     /**
180      * Requests the domain selection.
181      *
182      * @param attr Attributetes required to determine the domain.
183      * @param callback A callback to receive the response.
184      */
selectDomain(@onNull DomainSelectionService.SelectionAttributes attr, @NonNull TransportSelectorCallback callback)185     public void selectDomain(@NonNull DomainSelectionService.SelectionAttributes attr,
186             @NonNull TransportSelectorCallback callback) {
187         if (attr == null || callback == null) return;
188         if (DBG) logd("selectDomain");
189 
190         Executor e = mDomainSelectionService.getCachedExecutor();
191         e.execute(() -> mDomainSelectionService.onDomainSelection(attr, callback));
192     }
193 
194     /**
195      * Notifies the change in {@link ServiceState} for a specific slot.
196      *
197      * @param phone {@link Phone} which the state changed.
198      * @param serviceState Updated {@link ServiceState}.
199      */
updateServiceState(Phone phone, ServiceState serviceState)200     private void updateServiceState(Phone phone, ServiceState serviceState) {
201         if (phone == null || serviceState == null) return;
202         if (DBG) logd("updateServiceState phoneId=" + phone.getPhoneId());
203 
204         Executor e = mDomainSelectionService.getCachedExecutor();
205         e.execute(() -> mDomainSelectionService.onServiceStateUpdated(
206                 phone.getPhoneId(), phone.getSubId(), serviceState));
207     }
208 
209     /**
210      * Notifies the change in {@link BarringInfo} for a specific slot.
211      *
212      * @param phone {@link Phone} which the state changed.
213      * @param info Updated {@link BarringInfo}.
214      */
updateBarringInfo(Phone phone, BarringInfo info)215     private void updateBarringInfo(Phone phone, BarringInfo info) {
216         if (phone == null || info == null) return;
217         if (DBG) logd("updateBarringInfo phoneId=" + phone.getPhoneId());
218 
219         Executor e = mDomainSelectionService.getCachedExecutor();
220         e.execute(() -> mDomainSelectionService.onBarringInfoUpdated(
221                 phone.getPhoneId(), phone.getSubId(), info));
222     }
223 
224     /**
225      * Registers for the notification of {@link ServiceState} and {@link BarringInfo}.
226      *
227      * @param c {@link DomainSelectionConnection} for which the registration is requested.
228      */
registerForStateChange(DomainSelectionConnection c)229     private void registerForStateChange(DomainSelectionConnection c) {
230         Phone phone = c.getPhone();
231         int count = mConnectionCounts[phone.getPhoneId()];
232         if (count < 0) count = 0;
233 
234         mConnectionCounts[phone.getPhoneId()] = count + 1;
235         if (count > 0) return;
236 
237         phone.registerForServiceStateChanged(mHandler, EVENT_SERVICE_STATE_CHANGED, phone);
238         phone.mCi.registerForBarringInfoChanged(mHandler, EVENT_BARRING_INFO_CHANGED, phone);
239 
240         updateServiceState(phone, phone.getServiceStateTracker().getServiceState());
241         updateBarringInfo(phone, phone.mCi.getLastBarringInfo());
242     }
243 
244     /**
245      * Unregisters for the notification of {@link ServiceState} and {@link BarringInfo}.
246      *
247      * @param c {@link DomainSelectionConnection} for which the unregistration is requested.
248      */
unregisterForStateChange(DomainSelectionConnection c)249     private void unregisterForStateChange(DomainSelectionConnection c) {
250         Phone phone = c.getPhone();
251         int count = mConnectionCounts[phone.getPhoneId()];
252         if (count < 1) count = 1;
253 
254         mConnectionCounts[phone.getPhoneId()] = count - 1;
255         if (count > 1) return;
256 
257         phone.unregisterForServiceStateChanged(mHandler);
258         phone.mCi.unregisterForBarringInfoChanged(mHandler);
259     }
260 
261     /**
262      * Notifies the {@link DomainSelectionConnection} instances registered
263      * of the service disconnection.
264      */
notifyServiceDisconnected()265     private void notifyServiceDisconnected() {
266         for (DomainSelectionConnection c : mConnections) {
267             c.onServiceDisconnected();
268         }
269     }
270 
271     /**
272      * Gets the {@link Executor} which executes methods of {@link DomainSelectionService.}
273      * @return {@link Executor} instance.
274      */
getDomainSelectionServiceExecutor()275     public @NonNull Executor getDomainSelectionServiceExecutor() {
276         return mDomainSelectionService.getCachedExecutor();
277     }
278 
279     /**
280      * Dumps logcal log
281      */
dump(@onNull PrintWriter printWriter)282     public void dump(@NonNull PrintWriter printWriter) {
283         mLocalLog.dump(printWriter);
284     }
285 
logd(String msg)286     private void logd(String msg) {
287         Log.d(TAG, msg);
288     }
289 
logi(String msg)290     private void logi(String msg) {
291         Log.i(TAG, msg);
292         mLocalLog.log(msg);
293     }
294 
loge(String msg)295     private void loge(String msg) {
296         Log.e(TAG, msg);
297         mLocalLog.log(msg);
298     }
299 }
300