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 */ 16package com.android.internal.telephony; 17 18import android.content.ActivityNotFoundException; 19import android.content.BroadcastReceiver; 20import android.content.ComponentName; 21import android.content.Context; 22import android.content.Intent; 23import android.content.IntentFilter; 24import android.content.pm.PackageManager; 25import android.os.PersistableBundle; 26import android.telephony.CarrierConfigManager; 27import android.telephony.Rlog; 28import android.text.TextUtils; 29import android.util.LocalLog; 30import android.util.Log; 31 32import com.android.internal.util.ArrayUtils; 33import com.android.internal.util.IndentingPrintWriter; 34 35import java.io.FileDescriptor; 36import java.io.PrintWriter; 37import java.util.ArrayList; 38import java.util.Arrays; 39import java.util.HashMap; 40import java.util.HashSet; 41import java.util.List; 42import java.util.Map; 43import java.util.Set; 44 45import static android.telephony.CarrierConfigManager.KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY; 46import static android.telephony.CarrierConfigManager.KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY; 47 48/** 49 * This class act as an CarrierSignalling Agent. 50 * it load registered carrier signalling receivers from carrier config, cache the result to avoid 51 * repeated polling and send the intent to the interested receivers. 52 * Each CarrierSignalAgent is associated with a phone object. 53 */ 54public class CarrierSignalAgent { 55 56 private static final String LOG_TAG = CarrierSignalAgent.class.getSimpleName(); 57 private static final boolean DBG = true; 58 private static final boolean VDBG = Rlog.isLoggable(LOG_TAG, Log.VERBOSE); 59 private static final boolean WAKE = true; 60 private static final boolean NO_WAKE = false; 61 62 /** delimiters for parsing config of the form: pakName./receiverName : signal1, signal2,..*/ 63 private static final String COMPONENT_NAME_DELIMITER = "\\s*:\\s*"; 64 private static final String CARRIER_SIGNAL_DELIMITER = "\\s*,\\s*"; 65 66 /** Member variables */ 67 private final Phone mPhone; 68 69 /** 70 * This is a map of intent action -> array list of component name of statically registered 71 * carrier signal receivers(wakeup receivers). 72 * Those intents are declared in the Manifest files, aiming to wakeup broadcast receivers. 73 * Carrier apps should be careful when configuring the wake signal list to avoid unnecessary 74 * wakeup. 75 * @see CarrierConfigManager#KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY 76 */ 77 private final Map<String, List<ComponentName>> mCachedWakeSignalConfigs = new HashMap<>(); 78 79 /** 80 * This is a map of intent action -> array list of component name of dynamically registered 81 * carrier signal receivers(non-wakeup receivers). Those intents will not wake up the apps. 82 * Note Carrier apps should avoid configuring no wake signals in there Manifest files. 83 * @see CarrierConfigManager#KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY 84 */ 85 private final Map<String, List<ComponentName>> mCachedNoWakeSignalConfigs = new HashMap<>(); 86 87 /** 88 * This is a list of supported signals from CarrierSignalAgent 89 */ 90 private final Set<String> mCarrierSignalList = new HashSet<>(Arrays.asList( 91 TelephonyIntents.ACTION_CARRIER_SIGNAL_PCO_VALUE, 92 TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED, 93 TelephonyIntents.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED, 94 TelephonyIntents.ACTION_CARRIER_SIGNAL_RESET)); 95 96 private final LocalLog mErrorLocalLog = new LocalLog(20); 97 98 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 99 public void onReceive(Context context, Intent intent) { 100 String action = intent.getAction(); 101 if (DBG) log("CarrierSignalAgent receiver action: " + action); 102 if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) { 103 // notify carrier apps before cache get purged 104 if (mPhone.getIccCard() != null 105 && IccCardConstants.State.ABSENT == mPhone.getIccCard().getState()) { 106 notifyCarrierSignalReceivers( 107 new Intent(TelephonyIntents.ACTION_CARRIER_SIGNAL_RESET)); 108 } 109 loadCarrierConfig(); 110 } 111 } 112 }; 113 114 /** Constructor */ 115 public CarrierSignalAgent(Phone phone) { 116 mPhone = phone; 117 loadCarrierConfig(); 118 // reload configurations on CARRIER_CONFIG_CHANGED 119 mPhone.getContext().registerReceiver(mReceiver, 120 new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)); 121 } 122 123 /** 124 * load carrier config and cached the results into a hashMap action -> array list of components. 125 */ 126 private void loadCarrierConfig() { 127 CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext() 128 .getSystemService(Context.CARRIER_CONFIG_SERVICE); 129 PersistableBundle b = null; 130 if (configManager != null) { 131 b = configManager.getConfig(); 132 } 133 if (b != null) { 134 synchronized (mCachedWakeSignalConfigs) { 135 mCachedWakeSignalConfigs.clear(); 136 log("Loading carrier config: " + KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY); 137 parseAndCache(b.getStringArray(KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY), 138 mCachedWakeSignalConfigs); 139 } 140 141 synchronized (mCachedNoWakeSignalConfigs) { 142 mCachedNoWakeSignalConfigs.clear(); 143 log("Loading carrier config: " 144 + KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY); 145 parseAndCache(b.getStringArray(KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY), 146 mCachedNoWakeSignalConfigs); 147 } 148 } 149 } 150 151 /** 152 * Parse each config with the form {pakName./receiverName : signal1, signal2,.} and cached the 153 * result internally to avoid repeated polling 154 * @see #CARRIER_SIGNAL_DELIMITER 155 * @see #COMPONENT_NAME_DELIMITER 156 * @param configs raw information from carrier config 157 */ 158 private void parseAndCache(String[] configs, 159 Map<String, List<ComponentName>> cachedConfigs) { 160 if (!ArrayUtils.isEmpty(configs)) { 161 for (String config : configs) { 162 if (!TextUtils.isEmpty(config)) { 163 String[] splitStr = config.trim().split(COMPONENT_NAME_DELIMITER, 2); 164 if (splitStr.length == 2) { 165 ComponentName componentName = ComponentName 166 .unflattenFromString(splitStr[0]); 167 if (componentName == null) { 168 loge("Invalid component name: " + splitStr[0]); 169 continue; 170 } 171 String[] signals = splitStr[1].split(CARRIER_SIGNAL_DELIMITER); 172 for (String s : signals) { 173 if (!mCarrierSignalList.contains(s)) { 174 loge("Invalid signal name: " + s); 175 continue; 176 } 177 List<ComponentName> componentList = cachedConfigs.get(s); 178 if (componentList == null) { 179 componentList = new ArrayList<>(); 180 cachedConfigs.put(s, componentList); 181 } 182 componentList.add(componentName); 183 if (VDBG) { 184 logv("Add config " + "{signal: " + s 185 + " componentName: " + componentName + "}"); 186 } 187 } 188 } else { 189 loge("invalid config format: " + config); 190 } 191 } 192 } 193 } 194 } 195 196 /** 197 * Check if there are registered carrier broadcast receivers to handle the passing intent 198 */ 199 public boolean hasRegisteredReceivers(String action) { 200 return mCachedWakeSignalConfigs.containsKey(action) 201 || mCachedNoWakeSignalConfigs.containsKey(action); 202 } 203 204 /** 205 * Broadcast the intents explicitly. 206 * Some sanity check will be applied before broadcasting. 207 * - for non-wakeup(runtime) receivers, make sure the intent is not declared in their manifests 208 * and apply FLAG_EXCLUDE_STOPPED_PACKAGES to avoid wake-up 209 * - for wakeup(manifest) receivers, make sure there are matched receivers with registered 210 * intents. 211 * 212 * @param intent intent which signals carrier apps 213 * @param receivers a list of component name for broadcast receivers. 214 * Those receivers could either be statically declared in Manifest or 215 * registered during run-time. 216 * @param wakeup true indicate wakeup receivers otherwise non-wakeup receivers 217 */ 218 private void broadcast(Intent intent, List<ComponentName> receivers, boolean wakeup) { 219 final PackageManager packageManager = mPhone.getContext().getPackageManager(); 220 for (ComponentName name : receivers) { 221 Intent signal = new Intent(intent); 222 signal.setComponent(name); 223 224 if (wakeup && packageManager.queryBroadcastReceivers(signal, 225 PackageManager.MATCH_DEFAULT_ONLY).isEmpty()) { 226 loge("Carrier signal receivers are configured but unavailable: " 227 + signal.getComponent()); 228 return; 229 } 230 if (!wakeup && !packageManager.queryBroadcastReceivers(signal, 231 PackageManager.MATCH_DEFAULT_ONLY).isEmpty()) { 232 loge("Runtime signals shouldn't be configured in Manifest: " 233 + signal.getComponent()); 234 return; 235 } 236 237 signal.putExtra(PhoneConstants.SUBSCRIPTION_KEY, mPhone.getSubId()); 238 signal.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 239 if (!wakeup) signal.setFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES); 240 241 try { 242 mPhone.getContext().sendBroadcast(signal); 243 if (DBG) { 244 log("Sending signal " + signal.getAction() + ((signal.getComponent() != null) 245 ? " to the carrier signal receiver: " + signal.getComponent() : "")); 246 } 247 } catch (ActivityNotFoundException e) { 248 loge("Send broadcast failed: " + e); 249 } 250 } 251 } 252 253 /** 254 * Match the intent against cached tables to find a list of registered carrier signal 255 * receivers and broadcast the intent. 256 * @param intent broadcasting intent, it could belong to wakeup, non-wakeup signal list or both 257 * 258 */ 259 public void notifyCarrierSignalReceivers(Intent intent) { 260 List<ComponentName> receiverList; 261 262 synchronized (mCachedWakeSignalConfigs) { 263 receiverList = mCachedWakeSignalConfigs.get(intent.getAction()); 264 if (!ArrayUtils.isEmpty(receiverList)) { 265 broadcast(intent, receiverList, WAKE); 266 } 267 } 268 269 synchronized (mCachedNoWakeSignalConfigs) { 270 receiverList = mCachedNoWakeSignalConfigs.get(intent.getAction()); 271 if (!ArrayUtils.isEmpty(receiverList)) { 272 broadcast(intent, receiverList, NO_WAKE); 273 } 274 } 275 } 276 277 private void log(String s) { 278 Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s); 279 } 280 281 private void loge(String s) { 282 mErrorLocalLog.log(s); 283 Rlog.e(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s); 284 } 285 286 private void logv(String s) { 287 Rlog.v(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s); 288 } 289 290 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 291 IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); 292 pw.println("mCachedWakeSignalConfigs:"); 293 ipw.increaseIndent(); 294 for (Map.Entry<String, List<ComponentName>> entry : mCachedWakeSignalConfigs.entrySet()) { 295 pw.println("signal: " + entry.getKey() + " componentName list: " + entry.getValue()); 296 } 297 ipw.decreaseIndent(); 298 299 pw.println("mCachedNoWakeSignalConfigs:"); 300 ipw.increaseIndent(); 301 for (Map.Entry<String, List<ComponentName>> entry : mCachedNoWakeSignalConfigs.entrySet()) { 302 pw.println("signal: " + entry.getKey() + " componentName list: " + entry.getValue()); 303 } 304 ipw.decreaseIndent(); 305 306 pw.println("error log:"); 307 ipw.increaseIndent(); 308 mErrorLocalLog.dump(fd, pw, args); 309 ipw.decreaseIndent(); 310 } 311} 312