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