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