• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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