1 /*
2  * Copyright (C) 2020 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;
18 
19 import android.annotation.NonNull;
20 import android.os.Handler;
21 import android.os.Message;
22 import android.os.PersistableBundle;
23 import android.os.Registrant;
24 import android.os.RegistrantList;
25 import android.telephony.AnomalyReporter;
26 import android.telephony.CarrierConfigManager;
27 import android.telephony.ServiceState;
28 import android.telephony.TelephonyDisplayInfo;
29 import android.telephony.TelephonyManager;
30 import android.util.IndentingPrintWriter;
31 import android.util.LocalLog;
32 import android.util.Pair;
33 
34 import com.android.internal.telephony.flags.FeatureFlags;
35 import com.android.telephony.Rlog;
36 
37 import java.io.FileDescriptor;
38 import java.io.PrintWriter;
39 import java.util.Set;
40 import java.util.UUID;
41 
42 import javax.sip.InvalidArgumentException;
43 
44 /**
45  * The DisplayInfoController updates and broadcasts all changes to {@link TelephonyDisplayInfo}.
46  * It manages all the information necessary for display purposes. Clients can register for display
47  * info changes via {@link #registerForTelephonyDisplayInfoChanged} and obtain the current
48  * TelephonyDisplayInfo via {@link #getTelephonyDisplayInfo}.
49  */
50 public class DisplayInfoController extends Handler {
51     private final String mLogTag;
52     private final LocalLog mLocalLog = new LocalLog(128);
53 
54     private static final Set<Pair<Integer, Integer>> VALID_DISPLAY_INFO_SET = Set.of(
55             // LTE
56             Pair.create(TelephonyManager.NETWORK_TYPE_LTE,
57                     TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA),
58             Pair.create(TelephonyManager.NETWORK_TYPE_LTE,
59                     TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO),
60             Pair.create(TelephonyManager.NETWORK_TYPE_LTE,
61                     TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA),
62             Pair.create(TelephonyManager.NETWORK_TYPE_LTE,
63                     TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED),
64 
65             // NR
66             Pair.create(TelephonyManager.NETWORK_TYPE_NR,
67                     TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED)
68             );
69 
70     /** Event for service state changed (roaming). */
71     private static final int EVENT_SERVICE_STATE_CHANGED = 1;
72     /** Event for carrier config changed. */
73     private static final int EVENT_CARRIER_CONFIG_CHANGED = 2;
74 
75     @NonNull private final Phone mPhone;
76     @NonNull private final NetworkTypeController mNetworkTypeController;
77     @NonNull private final RegistrantList mTelephonyDisplayInfoChangedRegistrants =
78             new RegistrantList();
79     @NonNull private final FeatureFlags mFeatureFlags;
80     @NonNull private TelephonyDisplayInfo mTelephonyDisplayInfo;
81     @NonNull private ServiceState mServiceState;
82     @NonNull private PersistableBundle mConfigs;
83 
DisplayInfoController(@onNull Phone phone, @NonNull FeatureFlags featureFlags)84     public DisplayInfoController(@NonNull Phone phone, @NonNull FeatureFlags featureFlags) {
85         mPhone = phone;
86         mFeatureFlags = featureFlags;
87         mLogTag = "DIC-" + mPhone.getPhoneId();
88         mServiceState = mPhone.getServiceStateTracker().getServiceState();
89         mConfigs = new PersistableBundle();
90         try {
91             mConfigs = mPhone.getContext().getSystemService(CarrierConfigManager.class)
92                     .getConfigForSubId(mPhone.getSubId(),
93                             CarrierConfigManager.KEY_SHOW_ROAMING_INDICATOR_BOOL);
94         } catch (Exception ignored) {
95             // CarrierConfigLoader might not be available yet.
96             // Once it's available, configs will be updated through the listener.
97         }
98         mPhone.getServiceStateTracker()
99                 .registerForServiceStateChanged(this, EVENT_SERVICE_STATE_CHANGED, null);
100         mPhone.getContext().getSystemService(CarrierConfigManager.class)
101                 .registerCarrierConfigChangeListener(Runnable::run,
102                         (slotIndex, subId, carrierId, specificCarrierId) -> {
103                             if (slotIndex == mPhone.getPhoneId()) {
104                                 obtainMessage(EVENT_CARRIER_CONFIG_CHANGED).sendToTarget();
105                             }
106                         });
107         mTelephonyDisplayInfo = new TelephonyDisplayInfo(
108                 TelephonyManager.NETWORK_TYPE_UNKNOWN,
109                 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
110                 false);
111         mNetworkTypeController = new NetworkTypeController(phone, this, featureFlags);
112         // EVENT_UPDATE will transition from DefaultState to the current state
113         // and update the TelephonyDisplayInfo based on the current state.
114         mNetworkTypeController.sendMessage(NetworkTypeController.EVENT_UPDATE);
115     }
116 
117     /**
118      * @return the current TelephonyDisplayInfo
119      */
getTelephonyDisplayInfo()120     @NonNull public TelephonyDisplayInfo getTelephonyDisplayInfo() {
121         return mTelephonyDisplayInfo;
122     }
123 
124     /**
125      * Update TelephonyDisplayInfo based on network type and override network type, received from
126      * NetworkTypeController.
127      */
updateTelephonyDisplayInfo()128     public void updateTelephonyDisplayInfo() {
129         TelephonyDisplayInfo newDisplayInfo = new TelephonyDisplayInfo(
130                 mNetworkTypeController.getDataNetworkType(),
131                 mNetworkTypeController.getOverrideNetworkType(),
132                 isRoaming());
133         if (!newDisplayInfo.equals(mTelephonyDisplayInfo)) {
134             logl("TelephonyDisplayInfo changed from " + mTelephonyDisplayInfo + " to "
135                     + newDisplayInfo);
136             validateDisplayInfo(newDisplayInfo);
137             mTelephonyDisplayInfo = newDisplayInfo;
138             mTelephonyDisplayInfoChangedRegistrants.notifyRegistrants();
139             mPhone.notifyDisplayInfoChanged(mTelephonyDisplayInfo);
140         }
141     }
142 
143     /**
144      * Determine the roaming status for icon display only.
145      * If this is {@code true}, the roaming indicator will be shown, and if this is {@code false},
146      * the roaming indicator will not be shown.
147      * To get the actual roaming status, use {@link ServiceState#getRoaming()} instead.
148      *
149      * @return Whether the device is considered roaming for display purposes.
150      */
isRoaming()151     private boolean isRoaming() {
152         boolean roaming = mServiceState.getRoaming();
153         if (roaming && mFeatureFlags.hideRoamingIcon()
154                 && !mConfigs.getBoolean(CarrierConfigManager.KEY_SHOW_ROAMING_INDICATOR_BOOL)) {
155             logl("Override roaming for display due to carrier configs.");
156             roaming = false;
157         }
158         return roaming;
159     }
160 
161     /**
162      * Validate the display info and trigger anomaly report if needed.
163      *
164      * @param displayInfo The display info to validate.
165      */
validateDisplayInfo(@onNull TelephonyDisplayInfo displayInfo)166     private void validateDisplayInfo(@NonNull TelephonyDisplayInfo displayInfo) {
167         try {
168             if (displayInfo.getNetworkType() == TelephonyManager.NETWORK_TYPE_LTE_CA) {
169                 throw new InvalidArgumentException("LTE_CA is not a valid network type.");
170             }
171             if (displayInfo.getNetworkType() < TelephonyManager.NETWORK_TYPE_UNKNOWN
172                     && displayInfo.getNetworkType() > TelephonyManager.NETWORK_TYPE_NR) {
173                 throw new InvalidArgumentException("Invalid network type "
174                         + displayInfo.getNetworkType());
175             }
176             if (displayInfo.getOverrideNetworkType()
177                     != TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE
178                     && !VALID_DISPLAY_INFO_SET.contains(Pair.create(displayInfo.getNetworkType(),
179                     displayInfo.getOverrideNetworkType()))) {
180                 throw new InvalidArgumentException("Invalid network type override "
181                         + TelephonyDisplayInfo.overrideNetworkTypeToString(
182                                 displayInfo.getOverrideNetworkType())
183                         + " for " + TelephonyManager.getNetworkTypeName(
184                                 displayInfo.getNetworkType()));
185             }
186         } catch (InvalidArgumentException e) {
187             logel(e.getMessage());
188             AnomalyReporter.reportAnomaly(UUID.fromString("3aa92a2c-94ed-46a0-a744-d6b1dfec2a56"),
189                     e.getMessage(), mPhone.getCarrierId());
190         }
191     }
192 
193     /**
194      * Register for TelephonyDisplayInfo changed.
195      * @param h Handler to notify
196      * @param what msg.what when the message is delivered
197      * @param obj msg.obj when the message is delivered
198      */
registerForTelephonyDisplayInfoChanged(Handler h, int what, Object obj)199     public void registerForTelephonyDisplayInfoChanged(Handler h, int what, Object obj) {
200         Registrant r = new Registrant(h, what, obj);
201         mTelephonyDisplayInfoChangedRegistrants.add(r);
202     }
203 
204     /**
205      * Unregister for TelephonyDisplayInfo changed.
206      * @param h Handler to notify
207      */
unregisterForTelephonyDisplayInfoChanged(Handler h)208     public void unregisterForTelephonyDisplayInfoChanged(Handler h) {
209         mTelephonyDisplayInfoChangedRegistrants.remove(h);
210     }
211 
212     @Override
handleMessage(@onNull Message msg)213     public void handleMessage(@NonNull Message msg) {
214         switch (msg.what) {
215             case EVENT_SERVICE_STATE_CHANGED:
216                 mServiceState = mPhone.getServiceStateTracker().getServiceState();
217                 log("ServiceState updated, isRoaming=" + mServiceState.getRoaming());
218                 updateTelephonyDisplayInfo();
219                 break;
220             case EVENT_CARRIER_CONFIG_CHANGED:
221                 mConfigs = mPhone.getContext().getSystemService(CarrierConfigManager.class)
222                         .getConfigForSubId(mPhone.getSubId(),
223                                 CarrierConfigManager.KEY_SHOW_ROAMING_INDICATOR_BOOL);
224                 log("Carrier configs updated: " + mConfigs);
225                 updateTelephonyDisplayInfo();
226                 break;
227         }
228     }
229 
230     /**
231      * Log debug messages.
232      * @param s debug messages
233      */
log(@onNull String s)234     private void log(@NonNull String s) {
235         Rlog.d(mLogTag, s);
236     }
237 
238     /**
239      * Log error messages.
240      * @param s error messages
241      */
loge(@onNull String s)242     private void loge(@NonNull String s) {
243         Rlog.e(mLogTag, s);
244     }
245 
246     /**
247      * Log debug messages and also log into the local log.
248      * @param s debug messages
249      */
logl(@onNull String s)250     private void logl(@NonNull String s) {
251         log(s);
252         mLocalLog.log(s);
253     }
254 
255     /**
256      * Log error messages and also log into the local log.
257      * @param s debug messages
258      */
logel(@onNull String s)259     private void logel(@NonNull String s) {
260         loge(s);
261         mLocalLog.log(s);
262     }
263 
264     /**
265      * Dump the current state.
266      */
dump(FileDescriptor fd, PrintWriter printWriter, String[] args)267     public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
268         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
269         pw.println("DisplayInfoController:");
270         pw.println(" mPhone=" + mPhone.getPhoneName());
271         pw.println(" mTelephonyDisplayInfo=" + mTelephonyDisplayInfo.toString());
272         pw.flush();
273         pw.println("Local logs:");
274         pw.increaseIndent();
275         mLocalLog.dump(fd, pw, args);
276         pw.decreaseIndent();
277         pw.println(" ***************************************");
278         mNetworkTypeController.dump(fd, pw, args);
279         pw.flush();
280     }
281 }
282