转载请注明出处:https://blog.csdn.net/turtlejj/article/details/81567426,谢谢~
最近由于碰到了一个CTS的问题,涉及到了PhoneAccount,然而之前并没有接触过相关的内容。因此想在网上找些资料来看,结果发现并没有特别详细介绍PhoneAccount的文章,于是萌生了自己写一篇的想法。
废话不多说,现在就开始吧。
一、什么是PhoneAccount
/frameworks/base/telecom/java/android/telecom/PhoneAccount.java
/**
* Represents a distinct method to place or receive a phone call. Apps which can place calls and
* want those calls to be integrated into the dialer and in-call UI should build an instance of
* this class and register it with the system using {@link TelecomManager}.
* <p>
* {@link TelecomManager} uses registered {@link PhoneAccount}s to present the user with
* alternative options when placing a phone call. When building a {@link PhoneAccount}, the app
* should supply a valid {@link PhoneAccountHandle} that references the connection service
* implementation Telecom will use to interact with the app.
*/
以上是Android源码中对PhoneAccount的介绍,简单来说就是,PhoneAccount是用来接打电话的,我们可以使用TelecomManager里面的方法来创建一个PhoneAccount。同时,PhoneAccount还有一个唯一的标识,叫做PhoneAccountHandle,Telecom会通过PhoneAccountHandle所提供的ConnectionService信息来与App进行通信。
由于自己是做Android系统研发的,所以对第三方的App以及网络电话(Sip)不是特别了解,因此我们这里先以最常见的Sim卡类型的PhoneAccount来进行讲解。
我们的手机之所以可以通过Sim卡来拨打电话,同样也是因为系统会为每张当前插入手机的Sim卡都创建了一个PhoneAccount。Telecom通过Sim卡的PhoneAccount就可以与Dialer以及in-call UI进行通信,并成功完成电话的拨打以及接听。
那么下面我们就来详细介绍一下PhoneAccount以及PhoneAccountHandle。
二、PhoneAccountHandle详解
刚才说到了PhoneAccountHandle这个词,它到底是个什么东西呢?我们先来看代码
PhoneAccountHandle类有三个局部变量,分别是ComponentName,Id和UserHandle
/frameworks/base/telecomm/java/android/telecom/PhoneAccountHandle.java
public final class PhoneAccountHandle implements Parcelable {
private final ComponentName mComponentName;
private final String mId;
private final UserHandle mUserHandle;
......
public PhoneAccountHandle(
@NonNull ComponentName componentName,
@NonNull String id,
@NonNull UserHandle userHandle) {
checkParameters(componentName, userHandle);
mComponentName = componentName;
mId = id;
mUserHandle = userHandle;
}
......
}
下面我们一个一个来说:
1.ComponentName
ComponentName类的作用是用来指定一个应用组件,可指定的类型有Activity,Service,BroadcastReceiver或者ContentProvider。
我们知道,要想唯一地指定一个组件,需要包含这个组件的包名以及类名。同时也就是该类所包含的两个局部变量,mPackage和mClass。
对于我们拨打和接听电话而言,我们刚刚在最开始提到过,PhoneAccountHandle需要提供一个ConnectionService来使Telecom和App进行通信。因此,这里要指定的,应该是一个Service类型的组件。
/frameworks/base/core/java/android/content/ComponentName.java
/**
* Identifier for a specific application component
* ({@link android.app.Activity}, {@link android.app.Service},
* {@link android.content.BroadcastReceiver}, or
* {@link android.content.ContentProvider}) that is available. Two
* pieces of information, encapsulated here, are required to identify
* a component: the package (a String) it exists in, and the class (a String)
* name inside of that package.
*
*/
public final class ComponentName implements Parcelable, Cloneable, Comparable<ComponentName> {
private final String mPackage;
private final String mClass;
......
public ComponentName(@NonNull String pkg, @NonNull String cls) {
if (pkg == null) throw new NullPointerException("package name is null");
if (cls == null) throw new NullPointerException("class name is null");
mPackage = pkg;
mClass = cls;
}
......
}
2.Id
对于通过Sim卡创建的PhoneAccountHandle来说,id所存储的就是每张Sim卡的IccId,有的同学要问了,什么是IccId呢?
通俗的来讲,IccId就相当于我们每张Sim卡的身份证,是每张Sim卡的唯一标识。一般来说,我们中国运营商的Sim卡都是以8986XX为开头的20位数字串(有些特殊的卡会包含字母)。
其中,86代表中国(类似于打电话时,为什么中国号码前面都是+86)。而后面的XX,不同运营商对应着不同的值,其中移动卡可能为00 02 07等,联通卡可能为01 09等,电信卡可能为03 11等(每个运营商随着所发行的Sim卡数量的增多,为了保证IccId的唯一性,可能会添加号段)。
那么,使用PhoneAccountHandle使用IccId作为自己的id,就可以很大程度上的保证自己的唯一性,从而更好的作为PhoneAccount的唯一标识。
3. UserHandle
UserHandle又是什么呢?
众所周知,我们的Linux系统是支持多用户的。那么,我们的Android系统是基于Linux开发而来的,(原生的Android系统)同样也是支持多用户的。只不过,由于一些OEM厂商对Android系统进行了深度定制,有可能在Settings中把设置多用户的入口给取消掉了,因此,我们看不到,也无法设置多用户。不过一般我们都是自己一个人使用手机,因此,这个功能对于我们来说,其实也可有可无。
我们只需要知道,一般来说,我们的手机都会有一个userId为0的默认用户就可以了。
三、PhoneAccount详解
通过以上的讲解,相信大家已经对PhoneAccountHandle有了一个初步的了解。我们接下来,就开始介绍PhoneAccount。老样子,我们来看代码。
不难发现,PhoneAccount的构造方法是private类型的,也就是说,我们没办法直接调用其来构建PhoneAccount实例,那究竟该怎么创建PhoneAccount对象呢?这时,PhoneAccount的内部类Builder就派上了用场,不过,我们这里暂且先不提Builder的用法,等到后面讲解如何创建PhoneAccount时,再来细说。
/frameworks/base/telecomm/java/android/telecom/PhoneAccount.java
public final class PhoneAccount implements Parcelable {
......
// PhoneAccount的唯一标识
private final PhoneAccountHandle mAccountHandle;
// Scheme与line1Number拼接而成的Uri
private final Uri mAddress;
// Scheme与从卡中获取的MSISDN(或CDMA卡的MDN)拼接而成的Uri
private final Uri mSubscriptionAddress;
// 该PhoneAccount的所能支持的能力,后面会详细讲解
private final int mCapabilities;
// 高亮显示的颜色
private final int mHighlightColor;
// 该PhoneAccount的标签
private final CharSequence mLabel;
// 该PhoneAccount的简短描述
private final CharSequence mShortDescription;
// 所能支持的uriScheme
private final List<String> mSupportedUriSchemes;
// 所支持的AudioRoute,例如EARPIECE(听筒)、SPEAKER(扬声器)、WIRED_HEADSET(带话筒的耳机)、BLUETOOTH(蓝牙)
private final int mSupportedAudioRoutes;
// 图标
private final Icon mIcon;
private final Bundle mExtras;
// 是否可用
private boolean mIsEnabled;
// 组Id
private String mGroupId;
......
public static class Builder {
private PhoneAccountHandle mAccountHandle;
private Uri mAddress;
private Uri mSubscriptionAddress;
private int mCapabilities;
private int mSupportedAudioRoutes = CallAudioState.ROUTE_ALL;
private int mHighlightColor = NO_HIGHLIGHT_COLOR;
private CharSequence mLabel;
private CharSequence mShortDescription;
private List<String> mSupportedUriSchemes = new ArrayList<String>();
private Icon mIcon;
private Bundle mExtras;
private boolean mIsEnabled = false;
private String mGroupId = "";
......
/**
* Creates an instance of the {@link PhoneAccount.Builder} from an existing
* {@link PhoneAccount}.
*
* @param phoneAccount The {@link PhoneAccount} used to initialize the builder.
*/
public Builder(PhoneAccount phoneAccount) {
mAccountHandle = phoneAccount.getAccountHandle();
mAddress = phoneAccount.getAddress();
mSubscriptionAddress = phoneAccount.getSubscriptionAddress();
mCapabilities = phoneAccount.getCapabilities();
mHighlightColor = phoneAccount.getHighlightColor();
mLabel = phoneAccount.getLabel();
mShortDescription = phoneAccount.getShortDescription();
mSupportedUriSchemes.addAll(phoneAccount.getSupportedUriSchemes());
mIcon = phoneAccount.getIcon();
mIsEnabled = phoneAccount.isEnabled();
mExtras = phoneAccount.getExtras();
mGroupId = phoneAccount.getGroupId();
mSupportedAudioRoutes = phoneAccount.getSupportedAudioRoutes();
}
......
public PhoneAccount build() {
// If no supported URI schemes were defined, assume "tel" is supported.
if (mSupportedUriSchemes.isEmpty()) {
addSupportedUriScheme(SCHEME_TEL);
}
return new PhoneAccount(
mAccountHandle,
mAddress,
mSubscriptionAddress,
mCapabilities,
mIcon,
mHighlightColor,
mLabel,
mShortDescription,
mSupportedUriSchemes,
mExtras,
mSupportedAudioRoutes,
mIsEnabled,
mGroupId);
}
}
private PhoneAccount(
PhoneAccountHandle account,
Uri address,
Uri subscriptionAddress,
int capabilities,
Icon icon,
int highlightColor,
CharSequence label,
CharSequence shortDescription,
List<String> supportedUriSchemes,
Bundle extras,
int supportedAudioRoutes,
boolean isEnabled,
String groupId) {
mAccountHandle = account;
mAddress = address;
mSubscriptionAddress = subscriptionAddress;
mCapabilities = capabilities;
mIcon = icon;
mHighlightColor = highlightColor;
mLabel = label;
mShortDescription = shortDescription;
mSupportedUriSchemes = Collections.unmodifiab