1 /* 2 * Copyright (C) 2016 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 package com.android.internal.telephony; 17 18 import static android.telephony.CarrierConfigManager.KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY; 19 import static android.telephony.CarrierConfigManager.KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY; 20 21 import android.annotation.Nullable; 22 import android.content.ActivityNotFoundException; 23 import android.content.BroadcastReceiver; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.content.pm.PackageManager; 29 import android.net.ConnectivityManager; 30 import android.net.Network; 31 import android.os.AsyncResult; 32 import android.os.Build; 33 import android.os.Handler; 34 import android.os.Message; 35 import android.os.PersistableBundle; 36 import android.os.UserHandle; 37 import android.telephony.CarrierConfigManager; 38 import android.telephony.SubscriptionManager; 39 import android.telephony.TelephonyManager; 40 import android.telephony.data.ApnSetting; 41 import android.text.TextUtils; 42 import android.util.LocalLog; 43 import android.util.Log; 44 45 import com.android.internal.telephony.util.ArrayUtils; 46 import com.android.internal.util.IndentingPrintWriter; 47 import com.android.telephony.Rlog; 48 49 import java.io.FileDescriptor; 50 import java.io.PrintWriter; 51 import java.util.Arrays; 52 import java.util.HashMap; 53 import java.util.HashSet; 54 import java.util.Map; 55 import java.util.Set; 56 import java.util.stream.Collectors; 57 58 /** 59 * This class act as an CarrierSignalling Agent. 60 * it load registered carrier signalling receivers from carrier config, cache the result to avoid 61 * repeated polling and send the intent to the interested receivers. 62 * Each CarrierSignalAgent is associated with a phone object. 63 */ 64 public class CarrierSignalAgent extends Handler { 65 66 private static final String LOG_TAG = CarrierSignalAgent.class.getSimpleName(); 67 private static final boolean DBG = true; 68 private static final boolean VDBG = Rlog.isLoggable(LOG_TAG, Log.VERBOSE); 69 private static final boolean WAKE = true; 70 private static final boolean NO_WAKE = false; 71 72 /** delimiters for parsing config of the form: pakName./receiverName : signal1, signal2,..*/ 73 private static final String COMPONENT_NAME_DELIMITER = "\\s*:\\s*"; 74 private static final String CARRIER_SIGNAL_DELIMITER = "\\s*,\\s*"; 75 76 /** Member variables */ 77 private final Phone mPhone; 78 private boolean mDefaultNetworkAvail; 79 80 /** 81 * This is a map of intent action -> set of component name of statically registered 82 * carrier signal receivers(wakeup receivers). 83 * Those intents are declared in the Manifest files, aiming to wakeup broadcast receivers. 84 * Carrier apps should be careful when configuring the wake signal list to avoid unnecessary 85 * wakeup. Note we use Set as the entry value to compare config directly regardless of element 86 * order. 87 * @see CarrierConfigManager#KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY 88 */ 89 private Map<String, Set<ComponentName>> mCachedWakeSignalConfigs = new HashMap<>(); 90 91 /** 92 * This is a map of intent action -> set of component name of dynamically registered 93 * carrier signal receivers(non-wakeup receivers). Those intents will not wake up the apps. 94 * Note Carrier apps should avoid configuring no wake signals in there Manifest files. 95 * Note we use Set as the entry value to compare config directly regardless of element order. 96 * @see CarrierConfigManager#KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY 97 */ 98 private Map<String, Set<ComponentName>> mCachedNoWakeSignalConfigs = new HashMap<>(); 99 100 private static final int EVENT_REGISTER_DEFAULT_NETWORK_AVAIL = 0; 101 102 /** 103 * This is a list of supported signals from CarrierSignalAgent 104 */ 105 private static final Set<String> VALID_CARRIER_SIGNAL_ACTIONS = new HashSet<>(Arrays.asList( 106 TelephonyManager.ACTION_CARRIER_SIGNAL_PCO_VALUE, 107 TelephonyManager.ACTION_CARRIER_SIGNAL_REDIRECTED, 108 TelephonyManager.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED, 109 TelephonyManager.ACTION_CARRIER_SIGNAL_RESET, 110 TelephonyManager.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE)); 111 112 private static final Map<String, String> NEW_ACTION_TO_COMPAT_MAP = 113 new HashMap<String, String>() {{ 114 put(TelephonyManager.ACTION_CARRIER_SIGNAL_PCO_VALUE, 115 TelephonyIntents.ACTION_CARRIER_SIGNAL_PCO_VALUE); 116 put(TelephonyManager.ACTION_CARRIER_SIGNAL_REDIRECTED, 117 TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED); 118 put(TelephonyManager.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED, 119 TelephonyIntents.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED); 120 put(TelephonyManager.ACTION_CARRIER_SIGNAL_RESET, 121 TelephonyIntents.ACTION_CARRIER_SIGNAL_RESET); 122 put(TelephonyManager.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE, 123 TelephonyIntents.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE); 124 }}; 125 126 private static final Map<String, String> COMPAT_ACTION_TO_NEW_MAP = NEW_ACTION_TO_COMPAT_MAP 127 .entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey)); 128 129 private final LocalLog mErrorLocalLog = new LocalLog(16); 130 131 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 132 public void onReceive(Context context, Intent intent) { 133 String action = intent.getAction(); 134 if (DBG) log("CarrierSignalAgent receiver action: " + action); 135 if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) { 136 loadCarrierConfig(); 137 } 138 } 139 }; 140 141 private ConnectivityManager.NetworkCallback mNetworkCallback; 142 143 /** Constructor */ CarrierSignalAgent(Phone phone)144 public CarrierSignalAgent(Phone phone) { 145 mPhone = phone; 146 loadCarrierConfig(); 147 // reload configurations on CARRIER_CONFIG_CHANGED 148 mPhone.getContext().registerReceiver(mReceiver, 149 new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)); 150 mPhone.getCarrierActionAgent().registerForCarrierAction( 151 CarrierActionAgent.CARRIER_ACTION_REPORT_DEFAULT_NETWORK_STATUS, this, 152 EVENT_REGISTER_DEFAULT_NETWORK_AVAIL, null, false); 153 } 154 155 @Override handleMessage(Message msg)156 public void handleMessage(Message msg) { 157 switch (msg.what) { 158 case EVENT_REGISTER_DEFAULT_NETWORK_AVAIL: 159 AsyncResult ar = (AsyncResult) msg.obj; 160 if (ar.exception != null) { 161 Rlog.e(LOG_TAG, "Register default network exception: " + ar.exception); 162 return; 163 } 164 final ConnectivityManager connectivityMgr = mPhone.getContext() 165 .getSystemService(ConnectivityManager.class); 166 if ((boolean) ar.result) { 167 mNetworkCallback = new ConnectivityManager.NetworkCallback() { 168 @Override 169 public void onAvailable(Network network) { 170 // an optimization to avoid signaling on every default network switch. 171 if (!mDefaultNetworkAvail) { 172 if (DBG) log("Default network available: " + network); 173 Intent intent = new Intent(TelephonyManager 174 .ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE); 175 intent.putExtra( 176 TelephonyManager.EXTRA_DEFAULT_NETWORK_AVAILABLE, true); 177 notifyCarrierSignalReceivers(intent); 178 mDefaultNetworkAvail = true; 179 } 180 } 181 @Override 182 public void onLost(Network network) { 183 if (DBG) log("Default network lost: " + network); 184 Intent intent = new Intent(TelephonyManager 185 .ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE); 186 intent.putExtra( 187 TelephonyManager.EXTRA_DEFAULT_NETWORK_AVAILABLE, false); 188 notifyCarrierSignalReceivers(intent); 189 mDefaultNetworkAvail = false; 190 } 191 }; 192 connectivityMgr.registerDefaultNetworkCallback(mNetworkCallback, mPhone); 193 log("Register default network"); 194 195 } else if (mNetworkCallback != null) { 196 connectivityMgr.unregisterNetworkCallback(mNetworkCallback); 197 mNetworkCallback = null; 198 mDefaultNetworkAvail = false; 199 log("unregister default network"); 200 } 201 break; 202 default: 203 break; 204 } 205 } 206 207 /** 208 * load carrier config and cached the results into a hashMap action -> array list of components. 209 */ loadCarrierConfig()210 private void loadCarrierConfig() { 211 CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext() 212 .getSystemService(Context.CARRIER_CONFIG_SERVICE); 213 PersistableBundle b = null; 214 if (configManager != null) { 215 b = configManager.getConfigForSubId(mPhone.getSubId()); 216 } 217 if (b != null) { 218 synchronized (mCachedWakeSignalConfigs) { 219 log("Loading carrier config: " + KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY); 220 Map<String, Set<ComponentName>> config = parseAndCache( 221 b.getStringArray(KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY)); 222 // In some rare cases, up-to-date config could be fetched with delay and all signals 223 // have already been delivered the receivers from the default carrier config. 224 // To handle this raciness, we should notify those receivers (from old configs) 225 // and reset carrier actions. This should be done before cached Config got purged 226 // and written with the up-to-date value, Otherwise those receivers from the 227 // old config might lingers without properly clean-up. 228 if (!mCachedWakeSignalConfigs.isEmpty() 229 && !config.equals(mCachedWakeSignalConfigs)) { 230 if (VDBG) log("carrier config changed, reset receivers from old config"); 231 mPhone.getCarrierActionAgent().sendEmptyMessage( 232 CarrierActionAgent.CARRIER_ACTION_RESET); 233 } 234 mCachedWakeSignalConfigs = config; 235 } 236 237 synchronized (mCachedNoWakeSignalConfigs) { 238 log("Loading carrier config: " 239 + KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY); 240 Map<String, Set<ComponentName>> config = parseAndCache( 241 b.getStringArray(KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY)); 242 if (!mCachedNoWakeSignalConfigs.isEmpty() 243 && !config.equals(mCachedNoWakeSignalConfigs)) { 244 if (VDBG) log("carrier config changed, reset receivers from old config"); 245 mPhone.getCarrierActionAgent().sendEmptyMessage( 246 CarrierActionAgent.CARRIER_ACTION_RESET); 247 } 248 mCachedNoWakeSignalConfigs = config; 249 } 250 } 251 } 252 253 /** 254 * Parse each config with the form {pakName./receiverName : signal1, signal2,.} and cached the 255 * result internally to avoid repeated polling 256 * @see #CARRIER_SIGNAL_DELIMITER 257 * @see #COMPONENT_NAME_DELIMITER 258 * @param configs raw information from carrier config 259 */ parseAndCache(String[] configs)260 private Map<String, Set<ComponentName>> parseAndCache(String[] configs) { 261 Map<String, Set<ComponentName>> newCachedWakeSignalConfigs = new HashMap<>(); 262 if (!ArrayUtils.isEmpty(configs)) { 263 for (String config : configs) { 264 if (!TextUtils.isEmpty(config)) { 265 String[] splitStr = config.trim().split(COMPONENT_NAME_DELIMITER, 2); 266 if (splitStr.length == 2) { 267 ComponentName componentName = ComponentName 268 .unflattenFromString(splitStr[0]); 269 if (componentName == null) { 270 loge("Invalid component name: " + splitStr[0]); 271 continue; 272 } 273 String[] signals = splitStr[1].split(CARRIER_SIGNAL_DELIMITER); 274 for (String s : signals) { 275 if (!VALID_CARRIER_SIGNAL_ACTIONS.contains(s)) { 276 // It could be a legacy action in the com.android.internal.telephony 277 // namespace. If that's the case, translate it to the new actions. 278 if (COMPAT_ACTION_TO_NEW_MAP.containsKey(s)) { 279 s = COMPAT_ACTION_TO_NEW_MAP.get(s); 280 } else { 281 loge("Invalid signal name: " + s); 282 continue; 283 } 284 } 285 Set<ComponentName> componentList = newCachedWakeSignalConfigs.get(s); 286 if (componentList == null) { 287 componentList = new HashSet<>(); 288 newCachedWakeSignalConfigs.put(s, componentList); 289 } 290 componentList.add(componentName); 291 if (VDBG) { 292 logv("Add config " + "{signal: " + s 293 + " componentName: " + componentName + "}"); 294 } 295 } 296 } else { 297 loge("invalid config format: " + config); 298 } 299 } 300 } 301 } 302 return newCachedWakeSignalConfigs; 303 } 304 305 /** 306 * Check if there are registered carrier broadcast receivers to handle the passing intent 307 */ hasRegisteredReceivers(String action)308 public boolean hasRegisteredReceivers(String action) { 309 return mCachedWakeSignalConfigs.containsKey(action) 310 || mCachedNoWakeSignalConfigs.containsKey(action); 311 } 312 313 /** 314 * Broadcast the intents explicitly. 315 * Some correctness checks will be applied before broadcasting. 316 * - for non-wakeup(runtime) receivers, make sure the intent is not declared in their manifests 317 * and apply FLAG_EXCLUDE_STOPPED_PACKAGES to avoid wake-up 318 * - for wakeup(manifest) receivers, make sure there are matched receivers with registered 319 * intents. 320 * 321 * @param intent intent which signals carrier apps 322 * @param receivers a list of component name for broadcast receivers. 323 * Those receivers could either be statically declared in Manifest or 324 * registered during run-time. 325 * @param wakeup true indicate wakeup receivers otherwise non-wakeup receivers 326 */ broadcast(Intent intent, Set<ComponentName> receivers, boolean wakeup)327 private void broadcast(Intent intent, Set<ComponentName> receivers, boolean wakeup) { 328 final PackageManager packageManager = mPhone.getContext().getPackageManager(); 329 for (ComponentName name : receivers) { 330 Intent signal = new Intent(intent); 331 if (wakeup) { 332 signal.setComponent(name); 333 } else { 334 // Explicit intents won't reach dynamically registered receivers -- set the package 335 // instead. 336 signal.setPackage(name.getPackageName()); 337 } 338 339 if (wakeup && packageManager.queryBroadcastReceivers(signal, 340 PackageManager.MATCH_DEFAULT_ONLY).isEmpty()) { 341 loge("Carrier signal receivers are configured but unavailable: " 342 + signal.getComponent()); 343 continue; 344 } 345 if (!wakeup && !packageManager.queryBroadcastReceivers(signal, 346 PackageManager.MATCH_DEFAULT_ONLY).isEmpty()) { 347 loge("Runtime signals shouldn't be configured in Manifest: " 348 + signal.getComponent()); 349 continue; 350 } 351 352 SubscriptionManager.putSubscriptionIdExtra(signal, mPhone.getSubId()); 353 signal.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 354 if (!wakeup) signal.setFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES); 355 356 Intent compatIntent = null; 357 try { 358 if (mPhone.getContext().getPackageManager() 359 .getApplicationInfo(name.getPackageName(), 0).targetSdkVersion 360 <= Build.VERSION_CODES.R) { 361 compatIntent = createCompatIntent(signal); 362 } 363 } catch (PackageManager.NameNotFoundException e) { 364 // ignore, don't do anything special for compatibility 365 } 366 try { 367 Intent intentToSend = compatIntent == null ? signal : compatIntent; 368 mPhone.getContext().sendBroadcastAsUser(intentToSend, UserHandle.ALL); 369 if (DBG) { 370 log("Sending signal " + intentToSend.getAction() 371 + " to the carrier signal receiver: " + intentToSend.getComponent()); 372 } 373 } catch (ActivityNotFoundException e) { 374 loge("Send broadcast failed: " + e); 375 } 376 } 377 } 378 379 /** 380 * Match the intent against cached tables to find a list of registered carrier signal 381 * receivers and broadcast the intent. 382 * @param intent broadcasting intent, it could belong to wakeup, non-wakeup signal list or both 383 * 384 */ notifyCarrierSignalReceivers(Intent intent)385 public void notifyCarrierSignalReceivers(Intent intent) { 386 Set<ComponentName> receiverSet; 387 388 synchronized (mCachedWakeSignalConfigs) { 389 receiverSet = mCachedWakeSignalConfigs.get(intent.getAction()); 390 if (!ArrayUtils.isEmpty(receiverSet)) { 391 broadcast(intent, receiverSet, WAKE); 392 } 393 } 394 395 synchronized (mCachedNoWakeSignalConfigs) { 396 receiverSet = mCachedNoWakeSignalConfigs.get(intent.getAction()); 397 if (!ArrayUtils.isEmpty(receiverSet)) { 398 broadcast(intent, receiverSet, NO_WAKE); 399 } 400 } 401 } 402 createCompatIntent(Intent original)403 private static @Nullable Intent createCompatIntent(Intent original) { 404 String compatAction = NEW_ACTION_TO_COMPAT_MAP.get(original.getAction()); 405 if (compatAction == null) { 406 Rlog.i(LOG_TAG, "intent action " + original.getAction() + " does not have a" 407 + " compat alternative for component " + original.getComponent()); 408 return null; 409 } 410 Intent compatIntent = new Intent(original); 411 compatIntent.setAction(compatAction); 412 for (String extraKey : original.getExtras().keySet()) { 413 switch (extraKey) { 414 case TelephonyManager.EXTRA_REDIRECTION_URL: 415 compatIntent.putExtra(TelephonyIntents.EXTRA_REDIRECTION_URL, 416 original.getStringExtra(TelephonyManager.EXTRA_REDIRECTION_URL)); 417 break; 418 case TelephonyManager.EXTRA_DATA_FAIL_CAUSE: 419 compatIntent.putExtra(TelephonyIntents.EXTRA_ERROR_CODE, 420 original.getIntExtra(TelephonyManager.EXTRA_DATA_FAIL_CAUSE, -1)); 421 break; 422 case TelephonyManager.EXTRA_PCO_ID: 423 compatIntent.putExtra(TelephonyIntents.EXTRA_PCO_ID, 424 original.getIntExtra(TelephonyManager.EXTRA_PCO_ID, -1)); 425 break; 426 case TelephonyManager.EXTRA_PCO_VALUE: 427 compatIntent.putExtra(TelephonyIntents.EXTRA_PCO_VALUE, 428 original.getByteArrayExtra(TelephonyManager.EXTRA_PCO_VALUE)); 429 break; 430 case TelephonyManager.EXTRA_DEFAULT_NETWORK_AVAILABLE: 431 compatIntent.putExtra(TelephonyIntents.EXTRA_DEFAULT_NETWORK_AVAILABLE, 432 original.getBooleanExtra( 433 TelephonyManager.EXTRA_DEFAULT_NETWORK_AVAILABLE, false)); 434 break; 435 case TelephonyManager.EXTRA_APN_TYPE: 436 int apnType = original.getIntExtra(TelephonyManager.EXTRA_APN_TYPE, 437 ApnSetting.TYPE_DEFAULT); 438 compatIntent.putExtra(TelephonyIntents.EXTRA_APN_TYPE_INT, apnType); 439 compatIntent.putExtra(TelephonyIntents.EXTRA_APN_TYPE, 440 ApnSetting.getApnTypesStringFromBitmask(apnType)); 441 break; 442 case TelephonyManager.EXTRA_APN_PROTOCOL: 443 int apnProtocol = original.getIntExtra(TelephonyManager.EXTRA_APN_PROTOCOL, -1); 444 compatIntent.putExtra(TelephonyIntents.EXTRA_APN_PROTOCOL_INT, apnProtocol); 445 compatIntent.putExtra(TelephonyIntents.EXTRA_APN_PROTOCOL, 446 ApnSetting.getProtocolStringFromInt(apnProtocol)); 447 break; 448 } 449 } 450 return compatIntent; 451 } 452 log(String s)453 private void log(String s) { 454 Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s); 455 } 456 loge(String s)457 private void loge(String s) { 458 mErrorLocalLog.log(s); 459 Rlog.e(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s); 460 } 461 logv(String s)462 private void logv(String s) { 463 Rlog.v(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s); 464 } 465 dump(FileDescriptor fd, PrintWriter pw, String[] args)466 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 467 IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); 468 pw.println("mCachedWakeSignalConfigs:"); 469 ipw.increaseIndent(); 470 for (Map.Entry<String, Set<ComponentName>> entry : mCachedWakeSignalConfigs.entrySet()) { 471 pw.println("signal: " + entry.getKey() + " componentName list: " + entry.getValue()); 472 } 473 ipw.decreaseIndent(); 474 475 pw.println("mCachedNoWakeSignalConfigs:"); 476 ipw.increaseIndent(); 477 for (Map.Entry<String, Set<ComponentName>> entry : mCachedNoWakeSignalConfigs.entrySet()) { 478 pw.println("signal: " + entry.getKey() + " componentName list: " + entry.getValue()); 479 } 480 ipw.decreaseIndent(); 481 482 pw.println("mDefaultNetworkAvail: " + mDefaultNetworkAvail); 483 484 pw.println("error log:"); 485 ipw.increaseIndent(); 486 mErrorLocalLog.dump(fd, pw, args); 487 ipw.decreaseIndent(); 488 } 489 } 490