Home | History | Annotate | Download | only in telephony
      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