1 /*
2  * Copyright 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.internal.telephony.dataconnection;
18 
19 import android.annotation.NonNull;
20 import android.content.BroadcastReceiver;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.content.ServiceConnection;
26 import android.content.pm.PackageManager;
27 import android.os.Handler;
28 import android.os.IBinder;
29 import android.os.PersistableBundle;
30 import android.os.Registrant;
31 import android.os.RegistrantList;
32 import android.os.RemoteException;
33 import android.os.UserHandle;
34 import android.telephony.AccessNetworkConstants.AccessNetworkType;
35 import android.telephony.Annotation.ApnType;
36 import android.telephony.AnomalyReporter;
37 import android.telephony.CarrierConfigManager;
38 import android.telephony.data.ApnSetting;
39 import android.telephony.data.IQualifiedNetworksService;
40 import android.telephony.data.IQualifiedNetworksServiceCallback;
41 import android.telephony.data.QualifiedNetworksService;
42 import android.telephony.data.ThrottleStatus;
43 import android.text.TextUtils;
44 import android.util.SparseArray;
45 
46 import com.android.internal.telephony.Phone;
47 import com.android.telephony.Rlog;
48 
49 import java.util.ArrayList;
50 import java.util.Arrays;
51 import java.util.HashSet;
52 import java.util.List;
53 import java.util.Set;
54 import java.util.UUID;
55 import java.util.stream.Collectors;
56 
57 /**
58  * Access network manager manages the qualified/available networks for mobile data connection.
59  * It binds to the vendor's qualified networks service and actively monitors the qualified
60  * networks changes.
61  */
62 public class AccessNetworksManager extends Handler {
63     private final String mLogTag;
64     private static final boolean DBG = false;
65     private final UUID mAnomalyUUID = UUID.fromString("c2d1a639-00e2-4561-9619-6acf37d90590");
66     private String mLastBoundPackageName;
67 
68     static final int[] SUPPORTED_APN_TYPES = {
69             ApnSetting.TYPE_DEFAULT,
70             ApnSetting.TYPE_MMS,
71             ApnSetting.TYPE_FOTA,
72             ApnSetting.TYPE_IMS,
73             ApnSetting.TYPE_CBS,
74             ApnSetting.TYPE_SUPL,
75             ApnSetting.TYPE_EMERGENCY,
76             ApnSetting.TYPE_XCAP
77     };
78 
79     private final Phone mPhone;
80 
81     private final CarrierConfigManager mCarrierConfigManager;
82 
83     private IQualifiedNetworksService mIQualifiedNetworksService;
84 
85     private AccessNetworksManagerDeathRecipient mDeathRecipient;
86 
87     private String mTargetBindingPackageName;
88 
89     private QualifiedNetworksServiceConnection mServiceConnection;
90 
91     // Available networks. Key is the APN type.
92     private final SparseArray<int[]> mAvailableNetworks = new SparseArray<>();
93 
94     private final RegistrantList mQualifiedNetworksChangedRegistrants = new RegistrantList();
95 
96     private final Set<DataThrottler> mDataThrottlers = new HashSet<>();
97 
98     private final BroadcastReceiver mConfigChangedReceiver = new BroadcastReceiver() {
99         @Override
100         public void onReceive(Context context, Intent intent) {
101             final String action = intent.getAction();
102             if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)
103                     && mPhone.getPhoneId() == intent.getIntExtra(
104                     CarrierConfigManager.EXTRA_SLOT_INDEX, 0)) {
105                 // We should wait for carrier config changed event because the target binding
106                 // package name can come from the carrier config. Note that we still get this event
107                 // even when SIM is absent.
108                 if (DBG) log("Carrier config changed. Try to bind qualified network service.");
109                 bindQualifiedNetworksService();
110             }
111         }
112     };
113 
114     /**
115      * Registers the data throttler in order to receive APN status changes.
116      *
117      * @param dataThrottler the data throttler to register
118      */
registerDataThrottler(DataThrottler dataThrottler)119     public void registerDataThrottler(DataThrottler dataThrottler) {
120         this.post(() -> {
121             QualifiedNetworksServiceConnection serviceConnection = mServiceConnection;
122             this.mDataThrottlers.add(dataThrottler);
123             if (serviceConnection != null) {
124                 serviceConnection.registerDataThrottler(dataThrottler);
125             }
126         });
127     }
128 
129     /**
130      * Represents qualified network types list on a specific APN type.
131      */
132     public static class QualifiedNetworks {
133         public final @ApnType int apnType;
134         // The qualified networks in preferred order. Each network is a AccessNetworkType.
135         public final int[] qualifiedNetworks;
QualifiedNetworks(@pnType int apnType, int[] qualifiedNetworks)136         public QualifiedNetworks(@ApnType int apnType, int[] qualifiedNetworks) {
137             this.apnType = apnType;
138             this.qualifiedNetworks = qualifiedNetworks;
139         }
140 
141         @Override
toString()142         public String toString() {
143             List<String> accessNetworkStrings = new ArrayList<>();
144             for (int network : qualifiedNetworks) {
145                 accessNetworkStrings.add(AccessNetworkType.toString(network));
146             }
147             return "[QualifiedNetworks: apnType="
148                     + ApnSetting.getApnTypeString(apnType)
149                     + ", networks="
150                     + Arrays.stream(qualifiedNetworks)
151                     .mapToObj(type -> AccessNetworkType.toString(type))
152                     .collect(Collectors.joining(","))
153                     + "]";
154         }
155     }
156 
157     private class AccessNetworksManagerDeathRecipient implements IBinder.DeathRecipient {
158         @Override
binderDied()159         public void binderDied() {
160             // TODO: try to rebind the service.
161             String message = "Qualified network service " + mLastBoundPackageName + " died.";
162             loge(message);
163             AnomalyReporter.reportAnomaly(mAnomalyUUID, message);
164         }
165     }
166 
167     private final class QualifiedNetworksServiceConnection implements ServiceConnection {
168 
169         /**
170          * The APN throttle status callback is attached to the service connection so that they have
171          * the same life cycle.
172          */
173         @NonNull
174         private final ThrottleStatusChangedCallback mThrottleStatusCallback;
175 
QualifiedNetworksServiceConnection()176         QualifiedNetworksServiceConnection() {
177             mThrottleStatusCallback = new ThrottleStatusChangedCallback();
178         }
179 
180         @Override
onServiceConnected(ComponentName name, IBinder service)181         public void onServiceConnected(ComponentName name, IBinder service) {
182             if (DBG) log("onServiceConnected " + name);
183             mIQualifiedNetworksService = IQualifiedNetworksService.Stub.asInterface(service);
184             mDeathRecipient = new AccessNetworksManagerDeathRecipient();
185             mLastBoundPackageName = getQualifiedNetworksServicePackageName();
186 
187             try {
188                 service.linkToDeath(mDeathRecipient, 0 /* flags */);
189                 mIQualifiedNetworksService.createNetworkAvailabilityProvider(mPhone.getPhoneId(),
190                         new QualifiedNetworksServiceCallback());
191 
192                 registerDataThrottlersFirstTime();
193 
194             } catch (RemoteException e) {
195                 loge("Remote exception. " + e);
196             }
197         }
198 
199         @Override
onServiceDisconnected(ComponentName name)200         public void onServiceDisconnected(ComponentName name) {
201             if (DBG) log("onServiceDisconnected " + name);
202             unregisterForThrottleCallbacks();
203             mTargetBindingPackageName = null;
204         }
205 
206         /**
207          * Runs on all of the data throttlers when the service is connected
208          */
registerDataThrottlersFirstTime()209         private void registerDataThrottlersFirstTime() {
210             post(() -> {
211                 for (DataThrottler dataThrottler : mDataThrottlers) {
212                     dataThrottler.registerForThrottleStatusChanges(mThrottleStatusCallback);
213                 }
214             });
215         }
216 
registerDataThrottler(DataThrottler dataThrottler)217         private void registerDataThrottler(DataThrottler dataThrottler) {
218             post(() -> {
219                 dataThrottler.registerForThrottleStatusChanges(mThrottleStatusCallback);
220             });
221         }
222 
unregisterForThrottleCallbacks()223         private void unregisterForThrottleCallbacks() {
224             post(() -> {
225                 for (DataThrottler dataThrottler : mDataThrottlers) {
226                     dataThrottler.unregisterForThrottleStatusChanges(mThrottleStatusCallback);
227                 }
228             });
229         }
230     }
231 
232     private class ThrottleStatusChangedCallback implements DataThrottler.Callback {
233         @Override
onThrottleStatusChanged(List<ThrottleStatus> throttleStatuses)234         public void onThrottleStatusChanged(List<ThrottleStatus> throttleStatuses) {
235             post(() -> {
236                 try {
237                     List<ThrottleStatus> throttleStatusesBySlot =
238                             throttleStatuses
239                                     .stream()
240                                     .filter(x -> x.getSlotIndex() == mPhone.getPhoneId())
241                                     .collect(Collectors.toList());
242 
243                     mIQualifiedNetworksService.reportThrottleStatusChanged(mPhone.getPhoneId(),
244                             throttleStatusesBySlot);
245                 } catch (Exception ex) {
246                     loge("onThrottleStatusChanged", ex);
247                 }
248             });
249         }
250     }
251 
252     private final class QualifiedNetworksServiceCallback extends
253             IQualifiedNetworksServiceCallback.Stub {
254         @Override
onQualifiedNetworkTypesChanged(int apnTypes, int[] qualifiedNetworkTypes)255         public void onQualifiedNetworkTypesChanged(int apnTypes, int[] qualifiedNetworkTypes) {
256             log("onQualifiedNetworkTypesChanged. apnTypes = ["
257                     + ApnSetting.getApnTypesStringFromBitmask(apnTypes)
258                     + "], networks = [" + Arrays.stream(qualifiedNetworkTypes)
259                     .mapToObj(i -> AccessNetworkType.toString(i)).collect(Collectors.joining(","))
260                     + "]");
261             List<QualifiedNetworks> qualifiedNetworksList = new ArrayList<>();
262             for (int supportedApnType : SUPPORTED_APN_TYPES) {
263                 if ((apnTypes & supportedApnType) == supportedApnType) {
264                     // TODO: Verify the preference from data settings manager to make sure the order
265                     // of the networks do not violate users/carrier's preference.
266                     if (mAvailableNetworks.get(supportedApnType) != null) {
267                         if (Arrays.equals(mAvailableNetworks.get(supportedApnType),
268                                 qualifiedNetworkTypes)) {
269                             log("Available networks for "
270                                     + ApnSetting.getApnTypesStringFromBitmask(supportedApnType)
271                                     + " not changed.");
272                             continue;
273                         }
274                     }
275                     mAvailableNetworks.put(supportedApnType, qualifiedNetworkTypes);
276                     qualifiedNetworksList.add(new QualifiedNetworks(supportedApnType,
277                             qualifiedNetworkTypes));
278                 }
279             }
280 
281             if (!qualifiedNetworksList.isEmpty()) {
282                 mQualifiedNetworksChangedRegistrants.notifyResult(qualifiedNetworksList);
283             }
284         }
285     }
286 
287     /**
288      * Constructor
289      *
290      * @param phone The phone object
291      */
AccessNetworksManager(Phone phone)292     public AccessNetworksManager(Phone phone) {
293         mPhone = phone;
294         mCarrierConfigManager = (CarrierConfigManager) phone.getContext().getSystemService(
295                 Context.CARRIER_CONFIG_SERVICE);
296         mLogTag = "ANM-" + mPhone.getPhoneId();
297 
298         IntentFilter intentFilter = new IntentFilter();
299         intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
300         try {
301             Context contextAsUser = phone.getContext().createPackageContextAsUser(
302                 phone.getContext().getPackageName(), 0, UserHandle.ALL);
303             contextAsUser.registerReceiver(mConfigChangedReceiver, intentFilter,
304                 null /* broadcastPermission */, null);
305         } catch (PackageManager.NameNotFoundException e) {
306             loge("Package name not found: ", e);
307         }
308         bindQualifiedNetworksService();
309     }
310 
311     /**
312      * Find the qualified network service from configuration and binds to it. It reads the
313      * configuration from carrier config if it exists. If not, read it from resources.
314      */
bindQualifiedNetworksService()315     private void bindQualifiedNetworksService() {
316         post(() -> {
317             Intent intent = null;
318             String packageName = getQualifiedNetworksServicePackageName();
319             String className = getQualifiedNetworksServiceClassName();
320 
321             if (DBG) log("Qualified network service package = " + packageName);
322             if (TextUtils.isEmpty(packageName)) {
323                 loge("Can't find the binding package");
324                 return;
325             }
326 
327             if (TextUtils.isEmpty(className)) {
328                 intent = new Intent(QualifiedNetworksService.QUALIFIED_NETWORKS_SERVICE_INTERFACE);
329                 intent.setPackage(packageName);
330             } else {
331                 ComponentName cm = new ComponentName(packageName, className);
332                 intent = new Intent(QualifiedNetworksService.QUALIFIED_NETWORKS_SERVICE_INTERFACE)
333                         .setComponent(cm);
334             }
335 
336             if (TextUtils.equals(packageName, mTargetBindingPackageName)) {
337                 if (DBG) log("Service " + packageName + " already bound or being bound.");
338                 return;
339             }
340 
341             if (mIQualifiedNetworksService != null
342                     && mIQualifiedNetworksService.asBinder().isBinderAlive()) {
343                 // Remove the network availability updater and then unbind the service.
344                 try {
345                     mIQualifiedNetworksService.removeNetworkAvailabilityProvider(
346                             mPhone.getPhoneId());
347                 } catch (RemoteException e) {
348                     loge("Cannot remove network availability updater. " + e);
349                 }
350 
351                 mPhone.getContext().unbindService(mServiceConnection);
352             }
353 
354             try {
355                 mServiceConnection = new QualifiedNetworksServiceConnection();
356                 log("bind to " + packageName);
357                 if (!mPhone.getContext().bindService(intent, mServiceConnection,
358                         Context.BIND_AUTO_CREATE)) {
359                     loge("Cannot bind to the qualified networks service.");
360                     return;
361                 }
362                 mTargetBindingPackageName = packageName;
363             } catch (Exception e) {
364                 loge("Cannot bind to the qualified networks service. Exception: " + e);
365             }
366         });
367     }
368 
369     /**
370      * Get the qualified network service package.
371      *
372      * @return package name of the qualified networks service package. Return empty string when in
373      * legacy mode (i.e. Dedicated IWLAN data/network service is not supported).
374      */
getQualifiedNetworksServicePackageName()375     private String getQualifiedNetworksServicePackageName() {
376         // Read package name from the resource
377         String packageName = mPhone.getContext().getResources().getString(
378                 com.android.internal.R.string.config_qualified_networks_service_package);
379 
380         PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId());
381 
382         if (b != null) {
383             // If carrier config overrides it, use the one from carrier config
384             String carrierConfigPackageName =  b.getString(CarrierConfigManager
385                     .KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_PACKAGE_OVERRIDE_STRING);
386             if (!TextUtils.isEmpty(carrierConfigPackageName)) {
387                 if (DBG) log("Found carrier config override " + carrierConfigPackageName);
388                 packageName = carrierConfigPackageName;
389             }
390         }
391 
392         return packageName;
393     }
394 
395     /**
396      * Get the qualified network service class name.
397      *
398      * @return class name of the qualified networks service package.
399      */
getQualifiedNetworksServiceClassName()400     private String getQualifiedNetworksServiceClassName() {
401         // Read package name from the resource
402         String className = mPhone.getContext().getResources().getString(
403                 com.android.internal.R.string.config_qualified_networks_service_class);
404 
405         PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mPhone.getSubId());
406 
407         if (b != null) {
408             // If carrier config overrides it, use the one from carrier config
409             String carrierConfigClassName =  b.getString(CarrierConfigManager
410                     .KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_CLASS_OVERRIDE_STRING);
411             if (!TextUtils.isEmpty(carrierConfigClassName)) {
412                 if (DBG) log("Found carrier config override " + carrierConfigClassName);
413                 className = carrierConfigClassName;
414             }
415         }
416 
417         return className;
418     }
419 
getQualifiedNetworksList()420     private @NonNull List<QualifiedNetworks> getQualifiedNetworksList() {
421         List<QualifiedNetworks> qualifiedNetworksList = new ArrayList<>();
422         for (int i = 0; i < mAvailableNetworks.size(); i++) {
423             qualifiedNetworksList.add(new QualifiedNetworks(mAvailableNetworks.keyAt(i),
424                     mAvailableNetworks.valueAt(i)));
425         }
426 
427         return qualifiedNetworksList;
428     }
429 
430     /**
431      * Register for qualified networks changed event.
432      *
433      * @param h The target to post the event message to.
434      * @param what The event.
435      */
registerForQualifiedNetworksChanged(Handler h, int what)436     public void registerForQualifiedNetworksChanged(Handler h, int what) {
437         if (h != null) {
438             Registrant r = new Registrant(h, what, null);
439             mQualifiedNetworksChangedRegistrants.add(r);
440 
441             // Notify for the first time if there is already something in the available network
442             // list.
443             if (mAvailableNetworks.size() != 0) {
444                 r.notifyResult(getQualifiedNetworksList());
445             }
446         }
447     }
448 
449     /**
450      * Unregister for qualified networks changed event.
451      *
452      * @param h The handler
453      */
unregisterForQualifiedNetworksChanged(Handler h)454     public void unregisterForQualifiedNetworksChanged(Handler h) {
455         if (h != null) {
456             mQualifiedNetworksChangedRegistrants.remove(h);
457         }
458     }
459 
log(String s)460     private void log(String s) {
461         Rlog.d(mLogTag, s);
462     }
463 
loge(String s)464     private void loge(String s) {
465         Rlog.e(mLogTag, s);
466     }
467 
loge(String s, Exception ex)468     private void loge(String s, Exception ex) {
469         Rlog.e(mLogTag, s, ex);
470     }
471 
472 }
473