blob: e1125d9d503a35f0a023f5bc5b85a287dd7103f8 [file] [log] [blame]
fionaxua21a87b2016-12-13 17:15:11 -08001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.carrierdefaultapp;
17
18import android.content.Context;
19import android.content.Intent;
20import android.os.PersistableBundle;
21import android.telephony.CarrierConfigManager;
22import android.telephony.Rlog;
23import android.text.TextUtils;
24import android.util.Log;
25import android.util.Pair;
26
27import com.android.internal.telephony.TelephonyIntents;
28import com.android.internal.util.ArrayUtils;
29
30import java.util.ArrayList;
31import java.util.HashMap;
32import java.util.List;
33
34/**
35 * Default carrier app allows carrier customization. OEMs could configure a list
36 * of carrier actions defined in {@link com.android.carrierdefaultapp.CarrierActionUtils
37 * CarrierActionUtils} to act upon certain signal or even different args of the same signal.
38 * This allows different interpretations of the signal between carriers and could easily alter the
39 * app's behavior in a configurable way. This helper class loads and parses the carrier configs
40 * and return a list of predefined carrier actions for the given input signal.
41 */
42public class CustomConfigLoader {
43 // delimiters for parsing carrier configs of the form "arg1, arg2 : action1, action2"
44 private static final String INTRA_GROUP_DELIMITER = "\\s*,\\s*";
45 private static final String INTER_GROUP_DELIMITER = "\\s*:\\s*";
46
47 private static final String TAG = CustomConfigLoader.class.getSimpleName();
48 private static final boolean VDBG = Rlog.isLoggable(TAG, Log.VERBOSE);
49
50 /**
51 * loads and parses the carrier config, return a list of carrier action for the given signal
52 * @param context
53 * @param intent passing signal for config match
54 * @return a list of carrier action for the given signal based on the carrier config.
55 *
56 * Example: input intent TelephonyIntent.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED
57 * This intent allows fined-grained matching based on both intent type & extra values:
58 * apnType and errorCode.
59 * apnType read from passing intent is "default" and errorCode is 0x26 for example and
60 * returned carrier config from carrier_default_actions_on_redirection_string_array is
61 * {
62 * "default, 0x26:1,4", // 0x26(NETWORK_FAILURE)
63 * "default, 0x70:2,3" // 0x70(APN_TYPE_CONFLICT)
64 * }
65 * [1, 4] // 1(CARRIER_ACTION_DISABLE_METERED_APNS), 4(CARRIER_ACTION_SHOW_PORTAL_NOTIFICATION)
66 * returns as the action index list based on the matching rule.
67 */
68 public static List<Integer> loadCarrierActionList(Context context, Intent intent) {
69 CarrierConfigManager carrierConfigManager = (CarrierConfigManager) context.getSystemService(
70 Context.CARRIER_CONFIG_SERVICE);
71 // return an empty list if no match found
72 List<Integer> actionList = new ArrayList<>();
73 if (carrierConfigManager == null) {
74 Rlog.e(TAG, "load carrier config failure with carrier config manager uninitialized");
75 return actionList;
76 }
77 PersistableBundle b = carrierConfigManager.getConfig();
78 if (b != null) {
79 String[] configs = null;
80 // used for intents which allow fine-grained interpretation based on intent extras
81 String arg1 = null;
82 String arg2 = null;
83 switch (intent.getAction()) {
84 case TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED:
85 configs = b.getStringArray(CarrierConfigManager
86 .KEY_CARRIER_DEFAULT_ACTIONS_ON_REDIRECTION_STRING_ARRAY);
87 break;
88 case TelephonyIntents.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED:
89 configs = b.getStringArray(CarrierConfigManager
90 .KEY_CARRIER_DEFAULT_ACTIONS_ON_DCFAILURE_STRING_ARRAY);
91 arg1 = intent.getStringExtra(TelephonyIntents.EXTRA_APN_TYPE_KEY);
92 arg2 = intent.getStringExtra(TelephonyIntents.EXTRA_ERROR_CODE_KEY);
93 break;
94 default:
95 Rlog.e(TAG, "load carrier config failure with un-configured key: " +
96 intent.getAction());
97 break;
98 }
99 if (!ArrayUtils.isEmpty(configs)) {
100 for (String config : configs) {
101 // parse each config until find the matching one
102 matchConfig(config, arg1, arg2, actionList);
103 if (!actionList.isEmpty()) {
104 // return the first match
105 if (VDBG) Rlog.d(TAG, "found match action list: " + actionList.toString());
106 return actionList;
107 }
108 }
109 }
110 Rlog.d(TAG, "no matching entry for signal: " + intent.getAction() + "arg1: " + arg1
111 + "arg2: " + arg2);
112 }
113 return actionList;
114 }
115
116 /**
117 * Match based on the config's format and input args
118 * passing arg1, arg2 should match the format of the config
119 * case 1: config {actionIdx1, actionIdx2...} arg1 and arg2 must be null
120 * case 2: config {arg1, arg2 : actionIdx1, actionIdx2...} requires full match of non-null args
121 * case 3: config {arg1 : actionIdx1, actionIdx2...} only need to match arg1
122 *
123 * @param config action list config obtained from CarrierConfigManager
124 * @param arg1 first intent argument, set if required for config match
125 * @param arg2 second intent argument, set if required for config match
126 * @param actionList append each parsed action to the passing list
127 */
128 private static void matchConfig(String config, String arg1, String arg2,
129 List<Integer> actionList) {
130 String[] splitStr = config.trim().split(INTER_GROUP_DELIMITER, 2);
131 String actionStr = null;
132
133 if (splitStr.length == 1 && arg1 == null && arg2 == null) {
134 // case 1
135 actionStr = splitStr[0];
136 } else if (splitStr.length == 2 && arg1 != null && arg2 != null) {
137 // case 2
138 String[] args = splitStr[0].split(INTRA_GROUP_DELIMITER);
139 if (args.length == 2 && TextUtils.equals(arg1, args[0]) &&
140 TextUtils.equals(arg2, args[1])) {
141 actionStr = splitStr[1];
142 }
143 } else if ((splitStr.length == 2) && (arg1 != null) && (arg2 == null)) {
144 // case 3
145 String[] args = splitStr[0].split(INTRA_GROUP_DELIMITER);
146 if (args.length == 1 && TextUtils.equals(arg1, args[0])) {
147 actionStr = splitStr[1];
148 }
149 }
150 // convert from string -> action idx list if found a matching entry
151 String[] actions = null;
152 if (!TextUtils.isEmpty(actionStr)) {
153 actions = actionStr.split(INTRA_GROUP_DELIMITER);
154 }
155 if (!ArrayUtils.isEmpty(actions)) {
156 for (String idx : actions) {
157 try {
158 actionList.add(Integer.parseInt(idx));
159 } catch (NumberFormatException e) {
160 Rlog.e(TAG, "NumberFormatException(string: " + idx + " config:" + config + "): "
161 + e);
162 }
163 }
164 }
165 }
166}