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