Files
app_Settings/src/com/android/settings/network/MobileNetworkIntentConverter.java
Shen Lin f677ee600a Align SETTINGS_PACKAGE_NAME reference to a single place
This CL migrated some fragmented declarations of Settings packageName.
When we want to referece package name of Settings, we prefer using
Utils#SETTINGS_PACKAGE_NAME.

Bug: 262123382
Test: make Settings -j64
Change-Id: Ice6afd993d5991bb3c50f676c637b9a5487261f7
2022-12-12 04:56:28 +00:00

293 lines
12 KiB
Java

/*
* Copyright (C) 2022 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.settings.network;
import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.os.SystemClock;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.ims.ImsRcsManager;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.android.settings.Settings.MobileNetworkActivity;
import com.android.settings.SettingsActivity;
import com.android.settings.network.telephony.MobileNetworkUtils;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
/**
* A Java {@link Function} for conversion between {@link Intent} to Settings,
* and within Settings itself.
*/
public class MobileNetworkIntentConverter implements Function<Intent, Intent> {
private static final String TAG = "MobileNetworkIntentConverter";
private static final ComponentName sTargetComponent = ComponentName
.createRelative(SETTINGS_PACKAGE_NAME,
MobileNetworkActivity.class.getTypeName());
private static final String INTENT_TRAMPOLINE = "android.settings.SEARCH_RESULT_TRAMPOLINE";
/**
* These actions has better aligned with definitions within AndroidManifest.xml
*/
private static final String[] sPotentialActions = new String[]{
null,
Intent.ACTION_MAIN,
android.provider.Settings.ACTION_NETWORK_OPERATOR_SETTINGS,
android.provider.Settings.ACTION_DATA_ROAMING_SETTINGS,
android.provider.Settings.ACTION_MMS_MESSAGE_SETTING,
ImsRcsManager.ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN,
INTENT_TRAMPOLINE
};
private static final String RE_ROUTE_TAG = ":reroute:" + TAG;
private static final AtomicReference<String> mCachedClassName =
new AtomicReference<String>();
private final Context mAppContext;
private final ComponentName mComponent;
/**
* Constructor
* @param activity which receiving {@link Intent}
*/
public MobileNetworkIntentConverter(@NonNull Activity activity) {
mAppContext = activity.getApplicationContext();
mComponent = activity.getComponentName();
}
/**
* API defined by {@link Function}.
* @param fromIntent is the {@link Intent} for convert.
* @return {@link Intent} for sending internally within Settings.
* Return {@code null} when failure.
*/
public Intent apply(Intent fromIntent) {
long startTime = SystemClock.elapsedRealtimeNanos();
Intent potentialReqIntent = null;
if (isAttachedToExposedComponents()) {
potentialReqIntent = convertFromDeepLink(fromIntent);
} else if (mayRequireConvert(fromIntent)) {
potentialReqIntent = fromIntent;
} else {
return null;
}
final Intent reqIntent = potentialReqIntent;
String action = reqIntent.getAction();
// Find out the subscription ID of request.
final int subId = extractSubscriptionId(reqIntent);
// Prepare the arguments Bundle.
Function<Intent, Intent> ops = Function.identity();
if (TextUtils.equals(action,
android.provider.Settings.ACTION_NETWORK_OPERATOR_SETTINGS)
|| TextUtils.equals(action,
android.provider.Settings.ACTION_DATA_ROAMING_SETTINGS)
|| TextUtils.equals(action, INTENT_TRAMPOLINE)) {
// Accepted.
ops = ops.andThen(intent -> extractArguments(intent, subId))
.andThen(args -> rePackIntent(args, reqIntent))
.andThen(intent -> updateFragment(intent, mAppContext, subId));
} else if (TextUtils.equals(action,
android.provider.Settings.ACTION_MMS_MESSAGE_SETTING)) {
ops = ops.andThen(intent -> extractArguments(intent, subId))
.andThen(args -> convertMmsArguments(args))
.andThen(args -> rePackIntent(args, reqIntent))
.andThen(intent -> updateFragment(intent, mAppContext, subId));
} else if (TextUtils.equals(action,
ImsRcsManager.ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN)) {
ops = ops.andThen(intent -> extractArguments(intent, subId))
.andThen(args -> supportContactDiscoveryDialog(args, mAppContext, subId))
.andThen(args -> rePackIntent(args, reqIntent))
.andThen(intent -> updateFragment(intent, mAppContext, subId));
} else if ((sTargetComponent.compareTo(mComponent) == 0)
&& ((action == null) || Intent.ACTION_MAIN.equals(action))) {
Log.d(TAG, "Support default actions direct to this component");
ops = ops.andThen(intent -> extractArguments(intent, subId))
.andThen(args -> rePackIntent(args, reqIntent))
.andThen(intent -> replaceIntentAction(intent))
.andThen(intent -> updateFragment(intent, mAppContext, subId));
} else {
return null;
}
if (!isAttachedToExposedComponents()) {
ops = ops.andThen(intent -> configForReRoute(intent));
}
Intent result = ops.apply(reqIntent);
if (result != null) {
long endTime = SystemClock.elapsedRealtimeNanos();
Log.d(TAG, mComponent.toString() + " intent conversion: "
+ (endTime - startTime) + " ns");
}
return result;
}
@VisibleForTesting
protected boolean isAttachedToExposedComponents() {
return (sTargetComponent.compareTo(mComponent) == 0);
}
protected int extractSubscriptionId(Intent reqIntent) {
return reqIntent.getIntExtra(android.provider.Settings.EXTRA_SUB_ID,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
}
protected Bundle extractArguments(Intent reqIntent, int subId) {
// Duplicate from SettingsActivity#getIntent()
Bundle args = reqIntent.getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS);
Bundle result = (args != null) ? new Bundle(args) : new Bundle();
result.putParcelable("intent", reqIntent);
result.putInt(android.provider.Settings.EXTRA_SUB_ID, subId);
return result;
}
protected Bundle convertMmsArguments(Bundle args) {
// highlight "mms_message" preference.
args.putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY,
MobileNetworkActivity.EXTRA_MMS_MESSAGE);
return args;
}
@VisibleForTesting
protected boolean mayShowContactDiscoveryDialog(Context context, int subId) {
// If this activity was launched using ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN, show the
// associated dialog only if the opt-in has not been granted yet.
return MobileNetworkUtils.isContactDiscoveryVisible(context, subId)
// has the user already enabled this configuration?
&& !MobileNetworkUtils.isContactDiscoveryEnabled(context, subId);
}
protected Bundle supportContactDiscoveryDialog(Bundle args, Context context, int subId) {
boolean showDialog = mayShowContactDiscoveryDialog(context, subId);
Log.d(TAG, "maybeShowContactDiscoveryDialog subId=" + subId + ", show=" + showDialog);
args.putBoolean(MobileNetworkActivity.EXTRA_SHOW_CAPABILITY_DISCOVERY_OPT_IN,
showDialog);
return args;
}
protected Intent rePackIntent(Bundle args, Intent reqIntent) {
Intent intent = new Intent(reqIntent);
intent.setComponent(sTargetComponent);
intent.putExtra(android.provider.Settings.EXTRA_SUB_ID,
args.getInt(android.provider.Settings.EXTRA_SUB_ID));
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
return intent;
}
protected Intent replaceIntentAction(Intent intent) {
intent.setAction(android.provider.Settings.ACTION_NETWORK_OPERATOR_SETTINGS);
return intent;
}
@VisibleForTesting
protected CharSequence getFragmentTitle(Context context, int subId) {
SubscriptionInfo subInfo = SubscriptionUtil.getSubscriptionOrDefault(context, subId);
return SubscriptionUtil.getUniqueSubscriptionDisplayName(subInfo, context);
}
protected Intent updateFragment(Intent intent, Context context, int subId) {
if (intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE) == null) {
CharSequence title = getFragmentTitle(context, subId);
if (title != null) {
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE, title.toString());
}
}
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, getFragmentClass(context));
return intent;
}
protected String getFragmentClass(Context context) {
String className = mCachedClassName.get();
if (className != null) {
return className;
}
try {
ActivityInfo ai = context.getPackageManager()
.getActivityInfo(sTargetComponent, PackageManager.GET_META_DATA);
if (ai != null && ai.metaData != null) {
className = ai.metaData.getString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS);
if (className != null) {
mCachedClassName.set(className);
}
return className;
}
} catch (NameNotFoundException nnfe) {
// No recovery
Log.d(TAG, "Cannot get Metadata for: " + sTargetComponent.toString());
}
return null;
}
protected Intent configForReRoute(Intent intent) {
if (intent.hasExtra(RE_ROUTE_TAG)) {
Log.d(TAG, "Skip re-routed intent " + intent);
return null;
}
return intent.putExtra(RE_ROUTE_TAG, intent.getAction())
.setComponent(null);
}
protected static boolean mayRequireConvert(Intent intent) {
if (intent == null) {
return false;
}
final String action = intent.getAction();
return Arrays.stream(sPotentialActions).anyMatch(potentialAction ->
TextUtils.equals(action, potentialAction)
);
}
protected Intent convertFromDeepLink(Intent intent) {
if (intent == null) {
return null;
}
if (!TextUtils.equals(intent.getAction(),
android.provider.Settings.ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY)) {
return intent;
}
try {
return Intent.parseUri(intent.getStringExtra(
android.provider.Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI),
Intent.URI_INTENT_SCHEME);
} catch (URISyntaxException exception) {
Log.d(TAG, "Intent URI corrupted", exception);
}
return null;
}
}