script-astra/Android/Sdk/sources/android-35/android/telephony/DomainSelectionService.java
localadmin 4380f00a78 init
2025-01-20 18:15:20 +03:00

891 lines
34 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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 android.telephony;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.CancellationSignal;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.telephony.Annotation.DisconnectCauses;
import android.telephony.Annotation.PreciseDisconnectCauses;
import android.telephony.ims.ImsReasonInfo;
import android.text.TextUtils;
import android.util.Log;
import com.android.internal.telephony.IDomainSelectionServiceController;
import com.android.internal.telephony.IDomainSelector;
import com.android.internal.telephony.ITransportSelectorCallback;
import com.android.internal.telephony.ITransportSelectorResultCallback;
import com.android.internal.telephony.IWwanSelectorCallback;
import com.android.internal.telephony.IWwanSelectorResultCallback;
import com.android.internal.telephony.flags.Flags;
import com.android.internal.telephony.util.TelephonyUtils;
import com.android.telephony.Rlog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
* Base domain selection implementation.
* <p>
* Services that extend {@link DomainSelectionService} must register the service in their
* AndroidManifest.xml to be detected by the framework.
* <p>
* 1) The application must declare that they use the
* android.permission.BIND_DOMAIN_SELECTION_SERVICE permission.
* <p>
* 2) The DomainSelectionService definition in the manifest must follow this format:
* <pre>
* {@code
* ...
* <service android:name=".EgDomainSelectionService"
* android:permission="android.permission.BIND_DOMAIN_SELECTION_SERVICE" >
* <intent-filter>
* <action android:name="android.telephony.DomainSelectionService" />
* </intent-filter>
* </service>
* ...
* }
* </pre>
* <p>
* The ComponentName corresponding to this DomainSelectionService component MUST also be set
* as the system domain selection implementation in order to be bound.
* The system domain selection implementation is set in the device overlay for
* {@code config_domain_selection_service_component_name}
* in {@code packages/services/Telephony/res/values/config.xml}.
*
* @hide
*/
@SystemApi
@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
public abstract class DomainSelectionService extends Service {
private static final String LOG_TAG = "DomainSelectionService";
/**
* The intent that must be defined as an intent-filter in the AndroidManifest of the
* {@link DomainSelectionService}.
*
* @hide
*/
public static final String SERVICE_INTERFACE = "android.telephony.DomainSelectionService";
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = "SELECTOR_TYPE_",
value = {
SELECTOR_TYPE_CALLING,
SELECTOR_TYPE_SMS})
public @interface SelectorType {}
/** Indicates the domain selector type for calling. */
public static final int SELECTOR_TYPE_CALLING = 1;
/** Indicates the domain selector type for sms. */
public static final int SELECTOR_TYPE_SMS = 2;
/** Indicates that the modem can scan for emergency service as per modems implementation. */
public static final int SCAN_TYPE_NO_PREFERENCE = 0;
/** Indicates that the modem will scan for emergency service in limited service mode. */
public static final int SCAN_TYPE_LIMITED_SERVICE = 1;
/** Indicates that the modem will scan for emergency service in full service mode. */
public static final int SCAN_TYPE_FULL_SERVICE = 2;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = "SCAN_TYPE_",
value = {
SCAN_TYPE_NO_PREFERENCE,
SCAN_TYPE_LIMITED_SERVICE,
SCAN_TYPE_FULL_SERVICE})
public @interface EmergencyScanType {}
/**
* Contains attributes required to determine the domain for a telephony service.
*/
@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
public static final class SelectionAttributes implements Parcelable {
private static final String TAG = "SelectionAttributes";
private int mSlotIndex;
private int mSubId;
private @Nullable String mCallId;
private @Nullable Uri mAddress;
private @SelectorType int mSelectorType;
private boolean mIsVideoCall;
private boolean mIsEmergency;
private boolean mIsTestEmergencyNumber;
private boolean mIsExitedFromAirplaneMode;
private @Nullable ImsReasonInfo mImsReasonInfo;
private @PreciseDisconnectCauses int mCause;
private @Nullable EmergencyRegistrationResult mEmergencyRegistrationResult;
/**
* @param slotIndex The logical slot index.
* @param subscriptionId The subscription identifier.
* @param callId The call identifier.
* @param address The dialed address.
* @param selectorType Indicates the requested domain selector type.
* @param video Indicates it's a video call.
* @param emergency Indicates it's emergency service.
* @param isTest Indicates it's a test emergency number.
* @param exited {@code true} if the request caused the device to move out of airplane mode.
* @param imsReasonInfo The reason why the last PS attempt failed.
* @param cause The reason why the last CS attempt failed.
* @param regResult The current registration result for emergency services.
*/
private SelectionAttributes(int slotIndex, int subscriptionId, @Nullable String callId,
@Nullable Uri address, @SelectorType int selectorType,
boolean video, boolean emergency, boolean isTest, boolean exited,
@Nullable ImsReasonInfo imsReasonInfo, @PreciseDisconnectCauses int cause,
@Nullable EmergencyRegistrationResult regResult) {
mSlotIndex = slotIndex;
mSubId = subscriptionId;
mCallId = callId;
mAddress = address;
mSelectorType = selectorType;
mIsVideoCall = video;
mIsEmergency = emergency;
mIsTestEmergencyNumber = isTest;
mIsExitedFromAirplaneMode = exited;
mImsReasonInfo = imsReasonInfo;
mCause = cause;
mEmergencyRegistrationResult = regResult;
}
/**
* Copy constructor.
*
* @param s Source selection attributes.
* @hide
*/
public SelectionAttributes(@NonNull SelectionAttributes s) {
mSlotIndex = s.mSlotIndex;
mSubId = s.mSubId;
mCallId = s.mCallId;
mAddress = s.mAddress;
mSelectorType = s.mSelectorType;
mIsEmergency = s.mIsEmergency;
mIsTestEmergencyNumber = s.mIsTestEmergencyNumber;
mIsExitedFromAirplaneMode = s.mIsExitedFromAirplaneMode;
mImsReasonInfo = s.mImsReasonInfo;
mCause = s.mCause;
mEmergencyRegistrationResult = s.mEmergencyRegistrationResult;
}
/**
* Constructs a SelectionAttributes object from the given parcel.
*/
private SelectionAttributes(@NonNull Parcel in) {
readFromParcel(in);
}
/**
* @return The logical slot index.
*/
public int getSlotIndex() {
return mSlotIndex;
}
/**
* @return The subscription identifier.
*/
public int getSubscriptionId() {
return mSubId;
}
/**
* @return The call identifier.
*/
public @Nullable String getCallId() {
return mCallId;
}
/**
* @return The dialed address.
*/
public @Nullable Uri getAddress() {
return mAddress;
}
/**
* @return The domain selector type.
*/
public @SelectorType int getSelectorType() {
return mSelectorType;
}
/**
* @return {@code true} if the request is for a video call.
*/
public boolean isVideoCall() {
return mIsVideoCall;
}
/**
* @return {@code true} if the request is for emergency services.
*/
public boolean isEmergency() {
return mIsEmergency;
}
/**
* @return {@code true} if the dialed number is a test emergency number.
*/
public boolean isTestEmergencyNumber() {
return mIsTestEmergencyNumber;
}
/**
* @return {@code true} if the request caused the device to move out of airplane mode.
*/
public boolean isExitedFromAirplaneMode() {
return mIsExitedFromAirplaneMode;
}
/**
* @return The PS disconnect cause if trying over PS resulted in a failure and
* reselection is required.
*/
public @Nullable ImsReasonInfo getPsDisconnectCause() {
return mImsReasonInfo;
}
/**
* @return The CS disconnect cause if trying over CS resulted in a failure and
* reselection is required.
*/
public @PreciseDisconnectCauses int getCsDisconnectCause() {
return mCause;
}
/**
* @return The current registration state of cellular network.
*/
public @Nullable EmergencyRegistrationResult getEmergencyRegistrationResult() {
return mEmergencyRegistrationResult;
}
@Override
public @NonNull String toString() {
return "{ slotIndex=" + mSlotIndex
+ ", subId=" + mSubId
+ ", callId=" + mCallId
+ ", address=" + (Build.IS_DEBUGGABLE ? mAddress : "***")
+ ", type=" + mSelectorType
+ ", videoCall=" + mIsVideoCall
+ ", emergency=" + mIsEmergency
+ ", isTest=" + mIsTestEmergencyNumber
+ ", airplaneMode=" + mIsExitedFromAirplaneMode
+ ", reasonInfo=" + mImsReasonInfo
+ ", cause=" + mCause
+ ", regResult=" + mEmergencyRegistrationResult
+ " }";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SelectionAttributes that = (SelectionAttributes) o;
return mSlotIndex == that.mSlotIndex && mSubId == that.mSubId
&& TextUtils.equals(mCallId, that.mCallId)
&& equalsHandlesNulls(mAddress, that.mAddress)
&& mSelectorType == that.mSelectorType && mIsVideoCall == that.mIsVideoCall
&& mIsEmergency == that.mIsEmergency
&& mIsTestEmergencyNumber == that.mIsTestEmergencyNumber
&& mIsExitedFromAirplaneMode == that.mIsExitedFromAirplaneMode
&& equalsHandlesNulls(mImsReasonInfo, that.mImsReasonInfo)
&& mCause == that.mCause
&& equalsHandlesNulls(mEmergencyRegistrationResult,
that.mEmergencyRegistrationResult);
}
@Override
public int hashCode() {
return Objects.hash(mCallId, mAddress, mImsReasonInfo,
mIsVideoCall, mIsEmergency, mIsTestEmergencyNumber, mIsExitedFromAirplaneMode,
mEmergencyRegistrationResult, mSlotIndex, mSubId, mSelectorType, mCause);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeInt(mSlotIndex);
out.writeInt(mSubId);
out.writeString8(mCallId);
out.writeParcelable(mAddress, 0);
out.writeInt(mSelectorType);
out.writeBoolean(mIsVideoCall);
out.writeBoolean(mIsEmergency);
out.writeBoolean(mIsTestEmergencyNumber);
out.writeBoolean(mIsExitedFromAirplaneMode);
out.writeParcelable(mImsReasonInfo, 0);
out.writeInt(mCause);
out.writeParcelable(mEmergencyRegistrationResult, 0);
}
private void readFromParcel(@NonNull Parcel in) {
mSlotIndex = in.readInt();
mSubId = in.readInt();
mCallId = in.readString8();
mAddress = in.readParcelable(Uri.class.getClassLoader(),
android.net.Uri.class);
mSelectorType = in.readInt();
mIsVideoCall = in.readBoolean();
mIsEmergency = in.readBoolean();
mIsTestEmergencyNumber = in.readBoolean();
mIsExitedFromAirplaneMode = in.readBoolean();
mImsReasonInfo = in.readParcelable(ImsReasonInfo.class.getClassLoader(),
android.telephony.ims.ImsReasonInfo.class);
mCause = in.readInt();
mEmergencyRegistrationResult = in.readParcelable(
EmergencyRegistrationResult.class.getClassLoader(),
EmergencyRegistrationResult.class);
}
public static final @NonNull Creator<SelectionAttributes> CREATOR =
new Creator<SelectionAttributes>() {
@Override
public SelectionAttributes createFromParcel(@NonNull Parcel in) {
return new SelectionAttributes(in);
}
@Override
public SelectionAttributes[] newArray(int size) {
return new SelectionAttributes[size];
}
};
private static boolean equalsHandlesNulls(Object a, Object b) {
return (a == null) ? (b == null) : a.equals(b);
}
/**
* Builder class creating a new instance.
*/
@FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE)
public static final class Builder {
private final int mSlotIndex;
private final int mSubId;
private @Nullable String mCallId;
private @Nullable Uri mAddress;
private final @SelectorType int mSelectorType;
private boolean mIsVideoCall;
private boolean mIsEmergency;
private boolean mIsTestEmergencyNumber;
private boolean mIsExitedFromAirplaneMode;
private @Nullable ImsReasonInfo mImsReasonInfo;
private @PreciseDisconnectCauses int mCause;
private @Nullable EmergencyRegistrationResult mEmergencyRegistrationResult;
/**
* Default constructor for Builder.
*/
public Builder(int slotIndex, int subscriptionId, @SelectorType int selectorType) {
mSlotIndex = slotIndex;
mSubId = subscriptionId;
mSelectorType = selectorType;
}
/**
* Sets the call identifier.
*
* @param callId The call identifier.
* @return The same instance of the builder.
*/
public @NonNull Builder setCallId(@Nullable String callId) {
mCallId = callId;
return this;
}
/**
* Sets the dialed address.
*
* @param address The dialed address.
* @return The same instance of the builder.
*/
public @NonNull Builder setAddress(@Nullable Uri address) {
mAddress = address;
return this;
}
/**
* Sets whether it's a video call or not.
*
* @param isVideo Indicates it's a video call.
* @return The same instance of the builder.
*/
public @NonNull Builder setVideoCall(boolean isVideo) {
mIsVideoCall = isVideo;
return this;
}
/**
* Sets whether it's an emergency service or not.
*
* @param isEmergency Indicates it's emergency service.
* @return The same instance of the builder.
*/
public @NonNull Builder setEmergency(boolean isEmergency) {
mIsEmergency = isEmergency;
return this;
}
/**
* Sets whether it's a test emergency number or not.
*
* @param isTest Indicates it's a test emergency number.
* @return The same instance of the builder.
*/
public @NonNull Builder setTestEmergencyNumber(boolean isTest) {
mIsTestEmergencyNumber = isTest;
return this;
}
/**
* Sets whether the request caused the device to move out of airplane mode.
*
* @param exited {@code true} if the request caused the device to move out of
* airplane mode.
* @return The same instance of the builder.
*/
public @NonNull Builder setExitedFromAirplaneMode(boolean exited) {
mIsExitedFromAirplaneMode = exited;
return this;
}
/**
* Sets an optional reason why the last PS attempt failed.
*
* @param info The reason why the last PS attempt failed.
* @return The same instance of the builder.
*/
public @NonNull Builder setPsDisconnectCause(@Nullable ImsReasonInfo info) {
mImsReasonInfo = info;
return this;
}
/**
* Sets an optional reason why the last CS attempt failed.
*
* @param cause The reason why the last CS attempt failed.
* @return The same instance of the builder.
*/
public @NonNull Builder setCsDisconnectCause(@PreciseDisconnectCauses int cause) {
mCause = cause;
return this;
}
/**
* Sets the current registration result for emergency services.
*
* @param regResult The current registration result for emergency services.
* @return The same instance of the builder.
*/
public @NonNull Builder setEmergencyRegistrationResult(
@Nullable EmergencyRegistrationResult regResult) {
mEmergencyRegistrationResult = regResult;
return this;
}
/**
* Build the SelectionAttributes.
* @return The SelectionAttributes object.
*/
public @NonNull SelectionAttributes build() {
return new SelectionAttributes(mSlotIndex, mSubId, mCallId, mAddress,
mSelectorType, mIsVideoCall, mIsEmergency, mIsTestEmergencyNumber,
mIsExitedFromAirplaneMode, mImsReasonInfo,
mCause, mEmergencyRegistrationResult);
}
}
}
/**
* A wrapper class for ITransportSelectorCallback interface.
*/
private final class TransportSelectorCallbackWrapper implements TransportSelectorCallback {
private static final String TAG = "TransportSelectorCallbackWrapper";
private final @NonNull ITransportSelectorCallback mCallback;
private final @NonNull Executor mExecutor;
private @Nullable ITransportSelectorResultCallbackAdapter mResultCallback;
private @Nullable DomainSelectorWrapper mSelectorWrapper;
TransportSelectorCallbackWrapper(@NonNull ITransportSelectorCallback cb,
@NonNull Executor executor) {
mCallback = cb;
mExecutor = executor;
}
@Override
public void onCreated(@NonNull DomainSelector selector) {
try {
mSelectorWrapper = new DomainSelectorWrapper(selector, mExecutor);
mCallback.onCreated(mSelectorWrapper.getCallbackBinder());
} catch (Exception e) {
Rlog.e(TAG, "onCreated e=" + e);
}
}
@Override
public void onWlanSelected(boolean useEmergencyPdn) {
try {
mCallback.onWlanSelected(useEmergencyPdn);
} catch (Exception e) {
Rlog.e(TAG, "onWlanSelected e=" + e);
}
}
@Override
public void onWwanSelected(Consumer<WwanSelectorCallback> consumer) {
try {
mResultCallback = new ITransportSelectorResultCallbackAdapter(consumer, mExecutor);
mCallback.onWwanSelectedAsync(mResultCallback);
} catch (Exception e) {
Rlog.e(TAG, "onWwanSelected e=" + e);
executeMethodAsyncNoException(mExecutor,
() -> consumer.accept(null), TAG, "onWwanSelectedAsync-Exception");
}
}
@Override
public void onSelectionTerminated(@DisconnectCauses int cause) {
try {
mCallback.onSelectionTerminated(cause);
mSelectorWrapper = null;
} catch (Exception e) {
Rlog.e(TAG, "onSelectionTerminated e=" + e);
}
}
private class ITransportSelectorResultCallbackAdapter
extends ITransportSelectorResultCallback.Stub {
private final @NonNull Consumer<WwanSelectorCallback> mConsumer;
private final @NonNull Executor mExecutor;
ITransportSelectorResultCallbackAdapter(
@NonNull Consumer<WwanSelectorCallback> consumer,
@NonNull Executor executor) {
mConsumer = consumer;
mExecutor = executor;
}
@Override
public void onCompleted(@NonNull IWwanSelectorCallback cb) {
if (mConsumer == null) return;
WwanSelectorCallback callback = new WwanSelectorCallbackWrapper(cb, mExecutor);
executeMethodAsyncNoException(mExecutor,
() -> mConsumer.accept(callback), TAG, "onWwanSelectedAsync-Completed");
}
}
}
/**
* A wrapper class for IDomainSelector interface.
*/
private final class DomainSelectorWrapper {
private static final String TAG = "DomainSelectorWrapper";
private @NonNull IDomainSelector mCallbackBinder;
DomainSelectorWrapper(@NonNull DomainSelector cb, @NonNull Executor executor) {
mCallbackBinder = new IDomainSelectorAdapter(cb, executor);
}
private class IDomainSelectorAdapter extends IDomainSelector.Stub {
private final @NonNull WeakReference<DomainSelector> mDomainSelectorWeakRef;
private final @NonNull Executor mExecutor;
IDomainSelectorAdapter(@NonNull DomainSelector domainSelector,
@NonNull Executor executor) {
mDomainSelectorWeakRef =
new WeakReference<DomainSelector>(domainSelector);
mExecutor = executor;
}
@Override
public void reselectDomain(@NonNull SelectionAttributes attr) {
final DomainSelector domainSelector = mDomainSelectorWeakRef.get();
if (domainSelector == null) return;
executeMethodAsyncNoException(mExecutor,
() -> domainSelector.reselectDomain(attr), TAG, "reselectDomain");
}
@Override
public void finishSelection() {
final DomainSelector domainSelector = mDomainSelectorWeakRef.get();
if (domainSelector == null) return;
executeMethodAsyncNoException(mExecutor,
() -> domainSelector.finishSelection(), TAG, "finishSelection");
}
}
public @NonNull IDomainSelector getCallbackBinder() {
return mCallbackBinder;
}
}
/**
* A wrapper class for IWwanSelectorCallback and IWwanSelectorResultCallback.
*/
private final class WwanSelectorCallbackWrapper
implements WwanSelectorCallback, CancellationSignal.OnCancelListener {
private static final String TAG = "WwanSelectorCallbackWrapper";
private final @NonNull IWwanSelectorCallback mCallback;
private final @NonNull Executor mExecutor;
private @Nullable IWwanSelectorResultCallbackAdapter mResultCallback;
WwanSelectorCallbackWrapper(@NonNull IWwanSelectorCallback cb,
@NonNull Executor executor) {
mCallback = cb;
mExecutor = executor;
}
@Override
public void onCancel() {
try {
mCallback.onCancel();
} catch (Exception e) {
Rlog.e(TAG, "onCancel e=" + e);
}
}
@Override
public void onRequestEmergencyNetworkScan(@NonNull List<Integer> preferredNetworks,
@EmergencyScanType int scanType, boolean resetScan,
@NonNull CancellationSignal signal,
@NonNull Consumer<EmergencyRegistrationResult> consumer) {
try {
if (signal != null) signal.setOnCancelListener(this);
mResultCallback = new IWwanSelectorResultCallbackAdapter(consumer, mExecutor);
mCallback.onRequestEmergencyNetworkScan(
preferredNetworks.stream().mapToInt(Integer::intValue).toArray(),
scanType, resetScan, mResultCallback);
} catch (Exception e) {
Rlog.e(TAG, "onRequestEmergencyNetworkScan e=" + e);
}
}
@Override
public void onDomainSelected(@NetworkRegistrationInfo.Domain int domain,
boolean useEmergencyPdn) {
try {
mCallback.onDomainSelected(domain, useEmergencyPdn);
} catch (Exception e) {
Rlog.e(TAG, "onDomainSelected e=" + e);
}
}
private class IWwanSelectorResultCallbackAdapter
extends IWwanSelectorResultCallback.Stub {
private final @NonNull Consumer<EmergencyRegistrationResult> mConsumer;
private final @NonNull Executor mExecutor;
IWwanSelectorResultCallbackAdapter(
@NonNull Consumer<EmergencyRegistrationResult> consumer,
@NonNull Executor executor) {
mConsumer = consumer;
mExecutor = executor;
}
@Override
public void onComplete(@NonNull EmergencyRegistrationResult result) {
if (mConsumer == null) return;
executeMethodAsyncNoException(mExecutor,
() -> mConsumer.accept(result), TAG, "onScanComplete");
}
}
}
private final Object mExecutorLock = new Object();
/** Executor used to execute methods called remotely by the framework. */
private @NonNull Executor mExecutor;
/**
* Selects a calling domain given the SelectionAttributes of the call request.
* <p>
* When the framework generates a request to place a call, {@link #onDomainSelection}
* will be called in order to determine the domain (CS or PS). For PS calls, the transport
* (WWAN or WLAN) will also need to be determined.
* <p>
* Once the domain/transport has been selected or an error has occurred,
* {@link TransportSelectorCallback} must be used to communicate the result back
* to the framework.
*
* @param attr Required to determine the domain.
* @param callback The callback instance being registered.
*/
public abstract void onDomainSelection(@NonNull SelectionAttributes attr,
@NonNull TransportSelectorCallback callback);
/**
* Notifies the change in {@link ServiceState} for a specific logical slot index.
*
* @param slotIndex For which the state changed.
* @param subscriptionId For which the state changed.
* @param serviceState Updated {@link ServiceState}.
*/
public void onServiceStateUpdated(int slotIndex, int subscriptionId,
@NonNull ServiceState serviceState) {
}
/**
* Notifies the change in {@link BarringInfo} for a specific logical slot index.
*
* @param slotIndex For which the state changed.
* @param subscriptionId For which the state changed.
* @param info Updated {@link BarringInfo}.
*/
public void onBarringInfoUpdated(int slotIndex, int subscriptionId, @NonNull BarringInfo info) {
}
private final IBinder mDomainSelectionServiceController =
new IDomainSelectionServiceController.Stub() {
@Override
public void selectDomain(@NonNull SelectionAttributes attr,
@NonNull ITransportSelectorCallback callback) throws RemoteException {
executeMethodAsync(getCachedExecutor(),
() -> DomainSelectionService.this.onDomainSelection(attr,
new TransportSelectorCallbackWrapper(callback, getCachedExecutor())),
LOG_TAG, "onDomainSelection");
}
@Override
public void updateServiceState(int slotIndex, int subscriptionId,
@NonNull ServiceState serviceState) {
executeMethodAsyncNoException(getCachedExecutor(),
() -> DomainSelectionService.this.onServiceStateUpdated(slotIndex,
subscriptionId, serviceState), LOG_TAG, "onServiceStateUpdated");
}
@Override
public void updateBarringInfo(int slotIndex, int subscriptionId,
@NonNull BarringInfo info) {
executeMethodAsyncNoException(getCachedExecutor(),
() -> DomainSelectionService.this.onBarringInfoUpdated(slotIndex,
subscriptionId, info),
LOG_TAG, "onBarringInfoUpdated");
}
};
private static void executeMethodAsync(@NonNull Executor executor, @NonNull Runnable r,
@NonNull String tag, @NonNull String errorLogName) throws RemoteException {
try {
CompletableFuture.runAsync(
() -> TelephonyUtils.runWithCleanCallingIdentity(r), executor).join();
} catch (CancellationException | CompletionException e) {
Rlog.w(tag, "Binder - " + errorLogName + " exception: " + e.getMessage());
throw new RemoteException(e.getMessage());
}
}
private void executeMethodAsyncNoException(@NonNull Executor executor, @NonNull Runnable r,
@NonNull String tag, @NonNull String errorLogName) {
try {
CompletableFuture.runAsync(
() -> TelephonyUtils.runWithCleanCallingIdentity(r), executor);
} catch (CancellationException | CompletionException e) {
Rlog.w(tag, "Binder - " + errorLogName + " exception: " + e.getMessage());
}
}
/** @hide */
@Override
public final @Nullable IBinder onBind(@Nullable Intent intent) {
if (intent == null) return null;
if (SERVICE_INTERFACE.equals(intent.getAction())) {
Log.i(LOG_TAG, "DomainSelectionService Bound.");
return mDomainSelectionServiceController;
}
return null;
}
/**
* The Executor to use when calling callback methods from the framework.
* <p>
* By default, calls from the framework will use Binder threads to call these methods.
*
* @return an {@link Executor} used to execute methods called remotely by the framework.
*/
@SuppressLint("OnNameExpected")
public @NonNull Executor getCreateExecutor() {
return Runnable::run;
}
/**
* Gets the {@link Executor} which executes methods of this service.
* This method should be private when this service is implemented in a separated process
* other than telephony framework.
* @return {@link Executor} instance.
* @hide
*/
public final @NonNull Executor getCachedExecutor() {
synchronized (mExecutorLock) {
if (mExecutor == null) {
Executor e = getCreateExecutor();
mExecutor = (e != null) ? e : Runnable::run;
}
return mExecutor;
}
}
/**
* Returns a string representation of the domain.
* @param domain The domain.
* @return The name of the domain.
* @hide
*/
public static @NonNull String getDomainName(@NetworkRegistrationInfo.Domain int domain) {
return NetworkRegistrationInfo.domainToString(domain);
}
}