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