diff options
author | fionaxu <fionaxu@google.com> | 2016-12-13 17:15:11 -0800 |
---|---|---|
committer | fionaxu <fionaxu@google.com> | 2017-01-12 15:50:18 -0800 |
commit | a21a87b7a4153839cebb3152d0003ee86d7a5252 (patch) | |
tree | f041be8ac255572f6aa355d21454a9a4c46052b0 /packages/CarrierDefaultApp/src | |
parent | f84e5e8f767c872ec6d43f893f6860edac9b464e (diff) |
Default Carrier app for traffic mitigation
- have the basic function working, support traffic mitigation and
captive portal login
- support carrier customization, OEM could configure a list of carrier
actions to act upon certain signals
- unit test
Test: Manual test with live sim card & runtest --path
frameworks/base/packages/CarrierDefaultApp
Bug: 30958215
Change-Id: Ie99be3b95e8a1dd60fc51bef703836478fbde09d
Diffstat (limited to 'packages/CarrierDefaultApp/src')
4 files changed, 611 insertions, 0 deletions
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLaunchActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLaunchActivity.java new file mode 100644 index 000000000000..b7fde12f1d22 --- /dev/null +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLaunchActivity.java @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.carrierdefaultapp; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.ProgressDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.net.CaptivePortal; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkInfo; +import android.net.NetworkRequest; +import android.os.Bundle; +import android.telephony.CarrierConfigManager; +import android.telephony.Rlog; +import android.telephony.SubscriptionManager; +import android.text.TextUtils; +import android.net.ICaptivePortal; +import android.view.ContextThemeWrapper; +import android.view.WindowManager; +import com.android.carrierdefaultapp.R; +import com.android.internal.telephony.PhoneConstants; +import com.android.internal.telephony.TelephonyIntents; +import com.android.internal.util.ArrayUtils; + +import static android.net.CaptivePortal.APP_RETURN_DISMISSED; + +/** + * Activity that launches in response to the captive portal notification + * @see com.android.carrierdefaultapp.CarrierActionUtils#CARRIER_ACTION_SHOW_PORTAL_NOTIFICATION + * This activity requests network connection if there is no available one, launches the + * {@link com.android.captiveportallogin portalApp} and keeps track of the portal activation result. + */ +public class CaptivePortalLaunchActivity extends Activity { + private static final String TAG = CaptivePortalLaunchActivity.class.getSimpleName(); + private static final boolean DBG = true; + public static final int NETWORK_REQUEST_TIMEOUT_IN_MS = 5 * 1000; + + private ConnectivityManager mCm = null; + private ConnectivityManager.NetworkCallback mCb = null; + /* Progress dialogue when request network connection for captive portal */ + private AlertDialog mProgressDialog = null; + /* Alert dialogue when network request is timeout */ + private AlertDialog mAlertDialog = null; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mCm = ConnectivityManager.from(this); + // Check network connection before loading portal + Network network = getNetworkForCaptivePortal(); + NetworkInfo nwInfo = mCm.getNetworkInfo(network); + if (nwInfo == null || !nwInfo.isConnected()) { + if (DBG) logd("Network unavailable, request restricted connection"); + requestNetwork(getIntent()); + } else { + launchCaptivePortal(getIntent(), network); + } + } + + // show progress dialog during network connecting + private void showConnectingProgressDialog() { + mProgressDialog = new ProgressDialog(getApplicationContext()); + mProgressDialog.setMessage(getString(R.string.progress_dialogue_network_connection)); + mProgressDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + mProgressDialog.show(); + } + + // if network request is timeout, show alert dialog with two option: cancel & wait + private void showConnectionTimeoutAlertDialog() { + mAlertDialog = new AlertDialog.Builder(new ContextThemeWrapper(this, R.style.AlertDialog)) + .setMessage(getString(R.string.alert_dialogue_network_timeout)) + .setTitle(getString(R.string.alert_dialogue_network_timeout_title)) + .setNegativeButton(getString(R.string.quit), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + // cancel + dismissDialog(mAlertDialog); + finish(); + } + }) + .setPositiveButton(getString(R.string.wait), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + // wait, request network again + dismissDialog(mAlertDialog); + requestNetwork(getIntent()); + } + }) + .create(); + mAlertDialog.show(); + } + + private void requestNetwork(final Intent intent) { + NetworkRequest request = new NetworkRequest.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) + .build(); + + mCb = new ConnectivityManager.NetworkCallback() { + @Override + public void onAvailable(Network network) { + if (DBG) logd("Network available: " + network); + dismissDialog(mProgressDialog); + mCm.bindProcessToNetwork(network); + launchCaptivePortal(intent, network); + } + + @Override + public void onUnavailable() { + if (DBG) logd("Network unavailable"); + dismissDialog(mProgressDialog); + showConnectionTimeoutAlertDialog(); + } + }; + showConnectingProgressDialog(); + mCm.requestNetwork(request, mCb, NETWORK_REQUEST_TIMEOUT_IN_MS); + } + + private void releaseNetworkRequest() { + logd("release Network Request"); + if (mCb != null) { + mCm.unregisterNetworkCallback(mCb); + mCb = null; + } + } + + private void dismissDialog(AlertDialog dialog) { + if (dialog != null) { + dialog.dismiss(); + } + } + + private Network getNetworkForCaptivePortal() { + Network[] info = mCm.getAllNetworks(); + if (!ArrayUtils.isEmpty(info)) { + for (Network nw : info) { + final NetworkCapabilities nc = mCm.getNetworkCapabilities(nw); + if (nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) + && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { + return nw; + } + } + } + return null; + } + + private void launchCaptivePortal(final Intent intent, Network network) { + String redirectUrl = intent.getStringExtra(TelephonyIntents.EXTRA_REDIRECTION_URL_KEY); + int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, + SubscriptionManager.getDefaultVoiceSubscriptionId()); + if (TextUtils.isEmpty(redirectUrl) || !matchUrl(redirectUrl, subId)) { + loge("Launch portal fails due to incorrect redirection URL: " + + Rlog.pii(TAG, redirectUrl)); + return; + } + final Intent portalIntent = new Intent(ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN); + portalIntent.putExtra(ConnectivityManager.EXTRA_NETWORK, network); + portalIntent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL, + new CaptivePortal(new ICaptivePortal.Stub() { + @Override + public void appResponse(int response) { + logd("portal response code: " + response); + releaseNetworkRequest(); + if (response == APP_RETURN_DISMISSED) { + // Upon success http response code, trigger re-evaluation + CarrierActionUtils.applyCarrierAction( + CarrierActionUtils.CARRIER_ACTION_ENABLE_RADIO, intent, + getApplicationContext()); + CarrierActionUtils.applyCarrierAction( + CarrierActionUtils.CARRIER_ACTION_ENABLE_METERED_APNS, intent, + getApplicationContext()); + CarrierActionUtils.applyCarrierAction( + CarrierActionUtils.CARRIER_ACTION_CANCEL_ALL_NOTIFICATIONS, + intent, getApplicationContext()); + } + } + })); + portalIntent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL, redirectUrl); + portalIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_CLEAR_TASK); + if (DBG) logd("launching portal"); + startActivity(portalIntent); + finish(); + } + + // match configured redirection url + private boolean matchUrl(String url, int subId) { + CarrierConfigManager configManager = getApplicationContext() + .getSystemService(CarrierConfigManager.class); + String[] redirectURLs = configManager.getConfigForSubId(subId).getStringArray( + CarrierConfigManager.KEY_CARRIER_DEFAULT_REDIRECTION_URL_STRING_ARRAY); + if (ArrayUtils.isEmpty(redirectURLs)) { + if (DBG) logd("match is unnecessary without any configured redirection url"); + return true; + } + for (String redirectURL : redirectURLs) { + if (url.startsWith(redirectURL)) { + return true; + } + } + if (DBG) loge("no match found for configured redirection url"); + return false; + } + + private static void logd(String s) { + Rlog.d(TAG, s); + } + + private static void loge(String s) { + Rlog.d(TAG, s); + } +} diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java new file mode 100644 index 000000000000..db4890fc0187 --- /dev/null +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.carrierdefaultapp; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.util.Log; +import com.android.internal.telephony.PhoneConstants; +import com.android.carrierdefaultapp.R; +/** + * This util class provides common logic for carrier actions + */ +public class CarrierActionUtils { + private static final String TAG = CarrierActionUtils.class.getSimpleName(); + + private static final String PORTAL_NOTIFICATION_TAG = "CarrierDefault.Portal.Notification"; + private static final String NO_DATA_NOTIFICATION_TAG = "CarrierDefault.NoData.Notification"; + private static final int PORTAL_NOTIFICATION_ID = 0; + private static final int NO_DATA_NOTIFICATION_ID = 1; + private static boolean ENABLE = true; + + // A list of supported carrier action idx + public static final int CARRIER_ACTION_ENABLE_METERED_APNS = 0; + public static final int CARRIER_ACTION_DISABLE_METERED_APNS = 1; + public static final int CARRIER_ACTION_DISABLE_RADIO = 2; + public static final int CARRIER_ACTION_ENABLE_RADIO = 3; + public static final int CARRIER_ACTION_SHOW_PORTAL_NOTIFICATION = 4; + public static final int CARRIER_ACTION_SHOW_NO_DATA_SERVICE_NOTIFICATION = 5; + public static final int CARRIER_ACTION_CANCEL_ALL_NOTIFICATIONS = 6; + + public static void applyCarrierAction(int actionIdx, Intent intent, Context context) { + switch (actionIdx) { + case CARRIER_ACTION_ENABLE_METERED_APNS: + onEnableAllMeteredApns(intent, context); + break; + case CARRIER_ACTION_DISABLE_METERED_APNS: + onDisableAllMeteredApns(intent, context); + break; + case CARRIER_ACTION_DISABLE_RADIO: + onDisableRadio(intent, context); + break; + case CARRIER_ACTION_ENABLE_RADIO: + onEnableRadio(intent, context); + break; + case CARRIER_ACTION_SHOW_PORTAL_NOTIFICATION: + onShowCaptivePortalNotification(intent, context); + break; + case CARRIER_ACTION_SHOW_NO_DATA_SERVICE_NOTIFICATION: + onShowNoDataServiceNotification(context); + break; + case CARRIER_ACTION_CANCEL_ALL_NOTIFICATIONS: + onCancelAllNotifications(context); + break; + default: + loge("unsupported carrier action index: " + actionIdx); + } + } + + private static void onDisableAllMeteredApns(Intent intent, Context context) { + int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, + SubscriptionManager.getDefaultVoiceSubscriptionId()); + logd("onDisableAllMeteredApns subId: " + subId); + final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class); + telephonyMgr.carrierActionSetMeteredApnsEnabled(subId, !ENABLE); + } + + private static void onEnableAllMeteredApns(Intent intent, Context context) { + int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, + SubscriptionManager.getDefaultVoiceSubscriptionId()); + logd("onEnableAllMeteredApns subId: " + subId); + final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class); + telephonyMgr.carrierActionSetMeteredApnsEnabled(subId, ENABLE); + } + + private static void onDisableRadio(Intent intent, Context context) { + int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, + SubscriptionManager.getDefaultVoiceSubscriptionId()); + logd("onDisableRadio subId: " + subId); + final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class); + telephonyMgr.carrierActionSetRadioEnabled(subId, !ENABLE); + } + + private static void onEnableRadio(Intent intent, Context context) { + int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, + SubscriptionManager.getDefaultVoiceSubscriptionId()); + logd("onEnableRadio subId: " + subId); + final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class); + telephonyMgr.carrierActionSetRadioEnabled(subId, ENABLE); + } + + private static void onShowCaptivePortalNotification(Intent intent, Context context) { + logd("onShowCaptivePortalNotification"); + final NotificationManager notificationMgr = context.getSystemService( + NotificationManager.class); + Intent portalIntent = new Intent(context, CaptivePortalLaunchActivity.class); + portalIntent.putExtras(intent); + PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, portalIntent, + PendingIntent.FLAG_UPDATE_CURRENT); + Notification notification = getNotification(context, R.string.portal_notification_id, + R.string.portal_notification_detail, pendingIntent); + try { + notificationMgr.notify(PORTAL_NOTIFICATION_TAG, PORTAL_NOTIFICATION_ID, notification); + } catch (NullPointerException npe) { + loge("setNotificationVisible: " + npe); + } + } + + private static void onShowNoDataServiceNotification(Context context) { + logd("onShowNoDataServiceNotification"); + final NotificationManager notificationMgr = context.getSystemService( + NotificationManager.class); + Notification notification = getNotification(context, R.string.no_data_notification_id, + R.string.no_data_notification_detail, null); + try { + notificationMgr.notify(NO_DATA_NOTIFICATION_TAG, NO_DATA_NOTIFICATION_ID, notification); + } catch (NullPointerException npe) { + loge("setNotificationVisible: " + npe); + } + } + + private static void onCancelAllNotifications(Context context) { + logd("onCancelAllNotifications"); + final NotificationManager notificationMgr = context.getSystemService( + NotificationManager.class); + notificationMgr.cancelAll(); + } + + private static Notification getNotification(Context context, int titleId, int textId, + PendingIntent pendingIntent) { + Resources resources = context.getResources(); + Notification.Builder builder = new Notification.Builder(context) + .setContentTitle(resources.getString(titleId)) + .setContentText(resources.getString(textId)) + .setSmallIcon(R.drawable.ic_sim_card) + .setOngoing(true) + .setPriority(Notification.PRIORITY_HIGH) + .setDefaults(Notification.DEFAULT_ALL) + .setVisibility(Notification.VISIBILITY_PUBLIC) + .setLocalOnly(true) + .setWhen(System.currentTimeMillis()) + .setShowWhen(false); + + if (pendingIntent != null) { + builder.setContentIntent(pendingIntent); + } + return builder.build(); + } + + private static void logd(String s) { + Log.d(TAG, s); + } + + private static void loge(String s) { + Log.e(TAG, s); + } +} diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierDefaultBroadcastReceiver.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierDefaultBroadcastReceiver.java new file mode 100644 index 000000000000..bc0fa026fd77 --- /dev/null +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierDefaultBroadcastReceiver.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.carrierdefaultapp; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +import java.util.List; + +public class CarrierDefaultBroadcastReceiver extends BroadcastReceiver{ + private static final String TAG = CarrierDefaultBroadcastReceiver.class.getSimpleName(); + + @Override + public void onReceive(Context context, Intent intent) { + Log.d(TAG, "onReceive intent: " + intent.getAction()); + List<Integer> actionList = CustomConfigLoader.loadCarrierActionList(context, intent); + for (int actionIdx : actionList) { + Log.d(TAG, "apply carrier action idx: " + actionIdx); + CarrierActionUtils.applyCarrierAction(actionIdx, intent, context); + } + } +} diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java new file mode 100644 index 000000000000..e1125d9d503a --- /dev/null +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.carrierdefaultapp; + +import android.content.Context; +import android.content.Intent; +import android.os.PersistableBundle; +import android.telephony.CarrierConfigManager; +import android.telephony.Rlog; +import android.text.TextUtils; +import android.util.Log; +import android.util.Pair; + +import com.android.internal.telephony.TelephonyIntents; +import com.android.internal.util.ArrayUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * Default carrier app allows carrier customization. OEMs could configure a list + * of carrier actions defined in {@link com.android.carrierdefaultapp.CarrierActionUtils + * CarrierActionUtils} to act upon certain signal or even different args of the same signal. + * This allows different interpretations of the signal between carriers and could easily alter the + * app's behavior in a configurable way. This helper class loads and parses the carrier configs + * and return a list of predefined carrier actions for the given input signal. + */ +public class CustomConfigLoader { + // delimiters for parsing carrier configs of the form "arg1, arg2 : action1, action2" + private static final String INTRA_GROUP_DELIMITER = "\\s*,\\s*"; + private static final String INTER_GROUP_DELIMITER = "\\s*:\\s*"; + + private static final String TAG = CustomConfigLoader.class.getSimpleName(); + private static final boolean VDBG = Rlog.isLoggable(TAG, Log.VERBOSE); + + /** + * loads and parses the carrier config, return a list of carrier action for the given signal + * @param context + * @param intent passing signal for config match + * @return a list of carrier action for the given signal based on the carrier config. + * + * Example: input intent TelephonyIntent.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED + * This intent allows fined-grained matching based on both intent type & extra values: + * apnType and errorCode. + * apnType read from passing intent is "default" and errorCode is 0x26 for example and + * returned carrier config from carrier_default_actions_on_redirection_string_array is + * { + * "default, 0x26:1,4", // 0x26(NETWORK_FAILURE) + * "default, 0x70:2,3" // 0x70(APN_TYPE_CONFLICT) + * } + * [1, 4] // 1(CARRIER_ACTION_DISABLE_METERED_APNS), 4(CARRIER_ACTION_SHOW_PORTAL_NOTIFICATION) + * returns as the action index list based on the matching rule. + */ + public static List<Integer> loadCarrierActionList(Context context, Intent intent) { + CarrierConfigManager carrierConfigManager = (CarrierConfigManager) context.getSystemService( + Context.CARRIER_CONFIG_SERVICE); + // return an empty list if no match found + List<Integer> actionList = new ArrayList<>(); + if (carrierConfigManager == null) { + Rlog.e(TAG, "load carrier config failure with carrier config manager uninitialized"); + return actionList; + } + PersistableBundle b = carrierConfigManager.getConfig(); + if (b != null) { + String[] configs = null; + // used for intents which allow fine-grained interpretation based on intent extras + String arg1 = null; + String arg2 = null; + switch (intent.getAction()) { + case TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED: + configs = b.getStringArray(CarrierConfigManager + .KEY_CARRIER_DEFAULT_ACTIONS_ON_REDIRECTION_STRING_ARRAY); + break; + case TelephonyIntents.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED: + configs = b.getStringArray(CarrierConfigManager + .KEY_CARRIER_DEFAULT_ACTIONS_ON_DCFAILURE_STRING_ARRAY); + arg1 = intent.getStringExtra(TelephonyIntents.EXTRA_APN_TYPE_KEY); + arg2 = intent.getStringExtra(TelephonyIntents.EXTRA_ERROR_CODE_KEY); + break; + default: + Rlog.e(TAG, "load carrier config failure with un-configured key: " + + intent.getAction()); + break; + } + if (!ArrayUtils.isEmpty(configs)) { + for (String config : configs) { + // parse each config until find the matching one + matchConfig(config, arg1, arg2, actionList); + if (!actionList.isEmpty()) { + // return the first match + if (VDBG) Rlog.d(TAG, "found match action list: " + actionList.toString()); + return actionList; + } + } + } + Rlog.d(TAG, "no matching entry for signal: " + intent.getAction() + "arg1: " + arg1 + + "arg2: " + arg2); + } + return actionList; + } + + /** + * Match based on the config's format and input args + * passing arg1, arg2 should match the format of the config + * case 1: config {actionIdx1, actionIdx2...} arg1 and arg2 must be null + * case 2: config {arg1, arg2 : actionIdx1, actionIdx2...} requires full match of non-null args + * case 3: config {arg1 : actionIdx1, actionIdx2...} only need to match arg1 + * + * @param config action list config obtained from CarrierConfigManager + * @param arg1 first intent argument, set if required for config match + * @param arg2 second intent argument, set if required for config match + * @param actionList append each parsed action to the passing list + */ + private static void matchConfig(String config, String arg1, String arg2, + List<Integer> actionList) { + String[] splitStr = config.trim().split(INTER_GROUP_DELIMITER, 2); + String actionStr = null; + + if (splitStr.length == 1 && arg1 == null && arg2 == null) { + // case 1 + actionStr = splitStr[0]; + } else if (splitStr.length == 2 && arg1 != null && arg2 != null) { + // case 2 + String[] args = splitStr[0].split(INTRA_GROUP_DELIMITER); + if (args.length == 2 && TextUtils.equals(arg1, args[0]) && + TextUtils.equals(arg2, args[1])) { + actionStr = splitStr[1]; + } + } else if ((splitStr.length == 2) && (arg1 != null) && (arg2 == null)) { + // case 3 + String[] args = splitStr[0].split(INTRA_GROUP_DELIMITER); + if (args.length == 1 && TextUtils.equals(arg1, args[0])) { + actionStr = splitStr[1]; + } + } + // convert from string -> action idx list if found a matching entry + String[] actions = null; + if (!TextUtils.isEmpty(actionStr)) { + actions = actionStr.split(INTRA_GROUP_DELIMITER); + } + if (!ArrayUtils.isEmpty(actions)) { + for (String idx : actions) { + try { + actionList.add(Integer.parseInt(idx)); + } catch (NumberFormatException e) { + Rlog.e(TAG, "NumberFormatException(string: " + idx + " config:" + config + "): " + + e); + } + } + } + } +} |