[MT8766][Android12] 使用谷歌LPA实现ESIM功能的流程
文章目录
- 开发平台基本信息
- 问题描述
- 实现流程
- 其他问题
开发平台基本信息
芯片: MT8766
版本: Android 12
kernel: msm-4.19
问题描述
客户需要我们设备支持ESIM功能,5月份的时候在高通6125上面预研过ESIM功能,当时ESIM供应商是Links field,集成流程只是内置了ESIM厂商的apk,并且开启了feature以及默认给了一些权限,具体ESIM功能的实现都是在厂商的apk中,所以就没去深入研究ESIM功能。但是,由于Links field报价过高,所以这次又找了两家ESIM厂商,分别是鹏越与紫光;鹏越只把他们的ESIM芯片寄给了我们,然后让我们去找GMS实验室要LPA的相关资料,按照谷歌的标准流程操作,即可实现ESIM功能;而紫光则提供了一个他们的apk,并且要求给权限与feature,与之前的Links field类似。GMS实验室提供了一份资料,里面包含了3份文档以及一个apk。
三份文档的内容大致为:
- 1、如何集成LPA
- 2、如何实现overlay app
- 3、如何在开机向导和系统设置增加ESIM选项
ESIM功能可以简单理解为,ESIM厂家提供芯片,芯片分为贴片式跟拔插式,贴片式是贴到主板上,类似于各种单片机;而拔插式则是一张实体ESIM卡,长得跟普通的SIM卡一样,需要插入到卡槽中才能使用;然后,就到ESIM运营商购买ESIM卡号,安卓系统就可以通过谷歌LPA服务将购买的ESIM卡号下载到ESIM芯片中,下载完成启用ESIM卡就能正常使用了,一张ESIM芯片可以下载多个ESIM卡,但是,设备最多只能启用一张ESIM。
实现流程
在验证ESIM功能之前,可以先到设置-关于手机-sim卡详细信息
中查看是否有EID,EID是ESIM芯片的唯一标识,代表着ESIM的正常使用,如果设备无法读取到EID,得排查硬件通路以及modem。
ESIM功能都实现流程可以分为以下几个步骤:
- 启用euicc的feature
- 内置谷歌LPA服务到system/priv-app/
- 给LPA服务增加priv-app权限或者关闭权限校验
- 增加overlay app
- 在系统设置中,调起LPA服务下载ESIM卡号,并启用ESIM
framework中已经有euicc的feature,只需要拷贝到设备即可
--- a/device/mediateksample/custom_go/full_custom_go.mk
+++ b/device/mediateksample/custom_go/full_custom_go.mk
@@ -7,3 +7,11 @@ MTK_TARGET_PROJECT_FOLDER := $(LOCAL_PATH)-include $(MTK_TARGET_PROJECT_FOLDER)/vnd_$(MTK_TARGET_PROJECT).mkPRODUCT_NAME := full_custom_go+PRODUCT_COPY_FILES += \
+ frameworks/native/data/etc/android.hardware.telephony.euicc.xml:system/etc/permissions/android.hardware.telephony.euicc.xml \
+
内置谷歌LPA服务到system/priv-app/
--- a/device/mediateksample/p8_go/full_custom_go.mk
+++ b/device/mediateksample/p8_go/full_custom_go.mk
@@ -7,3 +7,11 @@ MTK_TARGET_PROJECT_FOLDER := $(LOCAL_PATH)-include $(MTK_TARGET_PROJECT_FOLDER)/vnd_$(MTK_TARGET_PROJECT).mkPRODUCT_NAME := full_custom_go
+
+PRODUCT_PACKAGES += \
+ EuiccGoogle \
+LOCAL_PATH := $(call my-dir)###############################################################################
include $(CLEAR_VARS)
LOCAL_MODULE := EuiccGoogle
LOCAL_SRC_FILES := EuiccGoogle.apk
LOCAL_MODULE_CLASS := APPS
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_PRIVILEGED_MODULE := true
LOCAL_MODULE_TAGS := optional
include $(BUILD_PREBUILT)
给LPA服务增加priv-app权限或者关闭权限校验,这里选择的是关闭权限校验
--- a/frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -3469,6 +3469,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {@NonNull PackageSetting packageSetting, @NonNull Permission permission) {if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_DISABLE) {return true;
+ }else if (!RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_DISABLE) {
+ return true;}final String packageName = pkg.getPackageName();if (Objects.equals(packageName, PLATFORM_PACKAGE_NAME)) {
增加overlay app,这个app是自己写的,需要实现几个功能
- 在AndroidManifest.xml中注册广播
<receiverandroid:name=".PartnerReceiver"android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"android:directBootAware="true"android:exported="true"><intent-filter><action android:name="com.google.android.euicc.action.PARTNER_CUSTOMIZATION" /></intent-filter></receiver>
- 在java文件中实现广播,并且广播方法中必须为空
package com.tp.euicc.overlay;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;public class PartnerReceiver extends BroadcastReceiver {public PartnerReceiver() {}@Overridepublic void onReceive(Context context, Intent intent) {}
}
- 在
res/values/strings.xml中增加两个字段
<string name="sim_slot_mappings_json" translatable="false">{"sim-slot-mappings":[{"devices":["custom_go"],"esim-slot-ids":[0],"psim-slot-ids":[1]}]}</string><integer name="download_type">3</integer>
谷歌LPA就是通过广播找到overlay app,然后从app的资源文件中获取sim_slot_mappings_json的值,拿到配置数据,其中devices的值要修改为Build.DEVICE,如果是其他项目移植要记得修改。后面的esim是虚拟sim卡,psim是物理sim卡,ids的值分别是对应的卡槽,比如在SIM 1卡槽接了ESIM,这里就配成0,不过实际测试,不过怎么配置ids,都能使用ESIM。
在系统设置中,调起LPA服务下载ESIM卡号,并启用ESIM
按照上面的方式集成之后,在系统设置-网络-移动网络选项,如果没有下载过ESIM,点击就会调起谷歌LPA服务,或者可以通过adb广播调起LPA配置界面,然后根据提示,扫描二维码,下载ESIM卡号即可。
adb shell am start -n
"com.google.android.euicc/com.android.euicc.ui.settings.CurrentProfileListActivity"
其他问题
之前在2290上面调试,出现下载完ESIM卡号之后,无法启用ESIM功能,需要在ESIM界面开、关一次飞行模式,ESIM启用按钮才可以点击。解决方案如下:
--- a/QSSI.12/packages/apps/Settings/src/com/android/settings/network/telephony/MobileNetworkSettings.java
+++ b/QSSI.12/packages/apps/Settings/src/com/android/settings/network/telephony/MobileNetworkSettings.java
@@ -58,6 +58,7 @@ import com.android.settingslib.search.SearchIndexable;import com.android.settingslib.utils.ThreadUtils;import org.codeaurora.internal.IExtTelephony;import java.util.Arrays;import java.util.List;
@@ -106,7 +107,8 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings {private void setScreenState() {int simState = mTelephonyManager.getSimState();
- boolean screenState = simState != TelephonyManager.SIM_STATE_ABSENT;
+ // boolean screenState = simState != TelephonyManager.SIM_STATE_ABSENT;
+ boolean screenState = true;if (screenState) {
在MTK8766中同样出现不可点击的问题,而且还把白卡显示出来,造成了混淆,解决方案如下:
--- a/vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/network/MobileNetworkListController.java
+++ b/vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/network/MobileNetworkListController.java
@@ -26,7 +26,9 @@ import android.content.Intent;import android.provider.Settings;import android.telephony.SubscriptionInfo;import android.telephony.SubscriptionManager;
+import android.telephony.euicc.EuiccManager;import android.util.ArrayMap;
+import android.util.Log;import androidx.lifecycle.Lifecycle;import androidx.lifecycle.LifecycleObserver;
@@ -122,7 +124,7 @@ public class MobileNetworkListController extends AbstractPreferenceController im} else {pref.setSummary(R.string.mobile_network_inactive_esim);/// M: Add for updating enabled state.
- pref.setEnabled(false);
+ //pref.setEnabled(false);}} else {if (mSubscriptionManager.isActiveSubscriptionId(subId)) {
@@ -130,7 +132,7 @@ public class MobileNetworkListController extends AbstractPreferenceController im} else if (SubscriptionUtil.showToggleForPhysicalSim(mSubscriptionManager)) {pref.setSummary(mContext.getString(R.string.mobile_network_inactive_sim));/// M: Add for updating enabled state.
- pref.setEnabled(false);
+ //pref.setEnabled(false);} else {pref.setSummary(mContext.getString(R.string.mobile_network_tap_to_activate,displayName));
@@ -138,7 +140,14 @@ public class MobileNetworkListController extends AbstractPreferenceController im}pref.setOnPreferenceClickListener(clickedPref -> {
- if (!info.isEmbedded() && !mSubscriptionManager.isActiveSubscriptionId(subId)
+ Log.d(TAG, "clickedPref isEmbedded: " + (info.isEmbedded()));
+ Log.d(TAG, "clickedPref isActiveSubscriptionId: " + (mSubscriptionManager.isActiveSubscriptionId(subId)));
+ Log.d(TAG, "clickedPref showToggleForPhysicalSim: " + (SubscriptionUtil.showToggleForPhysicalSim(mSubscriptionManager)));
+ /// M: Add for updating enabled state.
+ if(info.isEmbedded()){
+ Intent intent = new Intent(EuiccManager.ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS);
+ mContext.startActivity(intent);
+ }else if (!info.isEmbedded() && !mSubscriptionManager.isActiveSubscriptionId(subId)&& !SubscriptionUtil.showToggleForPhysicalSim(mSubscriptionManager)) {SubscriptionUtil.startToggleSubscriptionDialogActivity(mContext, subId, true);} else {
--- a/vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/network/MobileNetworkSummaryController.java
+++ b/vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/network/MobileNetworkSummaryController.java
@@ -258,8 +258,17 @@ public class MobileNetworkSummaryController extends AbstractPreferenceControllermPreference.setEnabled(false);}/// @}
- } else {
- mPreference.setFragment(MobileNetworkListFragment.class.getCanonicalName());
+ } else {
+ /// M: Add for updating enabled state.
+ mPreference.setOnPreferenceClickListener((Preference pref) -> {
+ logPreferenceClick(pref);
+
+ Intent intent = new Intent(EuiccManager.ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS);
+ mContext.startActivity(intent);
+ return true;
+ });
+
+ //mPreference.setFragment(MobileNetworkListFragment.class.getCanonicalName());}}
--- a/vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/network/telephony/MobileNetworkSettings.java
+++ b/vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/network/telephony/MobileNetworkSettings.java
@@ -27,6 +27,7 @@ import android.provider.Settings;import android.provider.SearchIndexableResource;import android.telephony.SubscriptionManager;import android.telephony.TelephonyManager;
+import android.telephony.euicc.EuiccManager;import android.text.TextUtils;import android.util.Log;import android.view.Menu;
@@ -100,8 +101,12 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings {return true;}final String key = preference.getKey();
+
+ Intent intent = new Intent(EuiccManager.ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS);
+ startActivity(intent);
+ return true;- if (TextUtils.equals(key, BUTTON_CDMA_SYSTEM_SELECT_KEY)
+ /*if (TextUtils.equals(key, BUTTON_CDMA_SYSTEM_SELECT_KEY)|| TextUtils.equals(key, BUTTON_CDMA_SUBSCRIPTION_KEY)) {if (mTelephonyManager.getEmergencyCallbackMode()) {startActivityForResult(
@@ -112,7 +117,7 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings {return true;}- return false;
+ return false;*/}@Override
diff --git a/vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/security/SimLockPreferenceController.java b/vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/security/SimLockPreferenceController.java
index 3b85888..98cbc9b 100644
--- a/vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/security/SimLockPreferenceController.java
+++ b/vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/security/SimLockPreferenceController.java
@@ -94,10 +94,10 @@ public class SimLockPreferenceController extends BasePreferenceController {for (SubscriptionInfo subInfo : subInfoList) {final int simState = mTelephonyManager.getSimState(subInfo.getSimSlotIndex());
- if ((simState != TelephonyManager.SIM_STATE_ABSENT)
- && (simState != TelephonyManager.SIM_STATE_UNKNOWN)) {
+ // if ((simState != TelephonyManager.SIM_STATE_ABSENT)
+ // && (simState != TelephonyManager.SIM_STATE_UNKNOWN)) {return true;
- }
+ // }}return false;}
MTK8766下载完ESIM卡号之后,有信号,能ping通百度,但是浏览器无法上网,最终定位是他们默认APN配置的问题
--- a/device/mediatek/config/apns-conf.xml
+++ b/device/mediatek/config/apns-conf.xml
@@ -25782,12 +25782,12 @@mcc="454"mnc="00"apn="mobile"
- proxy="192.168.59.51"
- port="8080"
- mmsc="http://192.168.58.171:8002"
- mmsproxy="192.168.59.51"
- mmsport="8080"
- type="default,supl,mms"
+ proxy=""
+ port=""
+ mmsc=""
+ mmsproxy=""
+ mmsport=""
+ type="default,ia,supl"protocol="IPV4V6"roaming_protocol="IPV4V6"/>
相关文章:
[MT8766][Android12] 使用谷歌LPA实现ESIM功能的流程
文章目录 开发平台基本信息问题描述实现流程 其他问题 开发平台基本信息 芯片: MT8766 版本: Android 12 kernel: msm-4.19 问题描述 客户需要我们设备支持ESIM功能,5月份的时候在高通6125上面预研过ESIM功能,当时ESIM供应商是Links field,…...

MyBatis-Plus为简化开发而生
简介 MyBatis-Plus 简称 MP是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。 他们的愿景是成为 MyBatis 最好的搭档,就像魂斗罗中的 1P、2P,基友搭配,效率翻倍。 特性 无…...

【翻译】Efficient Data Loader for Fast Sampling-Based GNN Training on Large Graphs
转载请注明出处:小锋学长生活大爆炸[xfxuezhang.cn] 此内容为机器翻译的结果,若有异议的地方,建议查看原文。 机器翻译的一些注意点,比如: 纪元、时代 > epoch工人 > worker火车、培训、训练师 > train Effic…...
OPUS解码器PLC
OPUS解码器支持PLC(Packet Loss Concealment)技术。 在音频通信中,网络丢包是常见的情况。当网络丢失一些音频数据包时,接收端可能无法正常解码并播放这些丢失的音频信号,导致声音中断或质量下降。为了改善这种情况&a…...

Rancher 使用指南
Rancher 使用指南 Rancher 是什么?Rancher 与 OpenShift / Kubesphere 主要区别对比RancherOpenShiftKubesphere 对比 Rancher 和 OpenShift Rancher 安装 Rancher 是什么? 企业级Kubernetes管理平台 Rancher 是供采用容器的团队使用的完整软件堆栈。它解决了管理多个Kuber…...

百度SEO优化全攻略(提高网站排名的5个方面)
百度SEO入门介绍: 随着互联网的不断发展,SEO已经成为网站优化的重要一环。而百度作为中国最大的搜索引擎,其SEO优化更是至关重要。SEO不仅能够提高网站排名,还能够提高网站流量、用户体验以及品牌知名度。因此,掌握百…...

华为云云耀云服务器L实例评测|华为云耀云服务器L实例私有库搭建verdaccio(八)
九、华为云耀云服务器L实例私有库搭建verdaccio: Verdaccio 是一个简单的、零配置本地私有 npm 软件包代理注册表。Verdaccio 开箱即用,拥有自己的小型数据库,能够代理其它注册表(例如 npmjs.org),缓存下载…...

C语言之动态内存管理_柔性数组篇(2)
目录 柔性数组的特点 柔性数组的使用 动态内存函数增容柔性数组模拟实现 柔性数组的优势 今天接着来讲解一下柔性数组知识。 柔性数组的特点 C99中,结构中的最后一个元素允许是未知大小的数组,这就叫做【柔性数组】成员。 结构体中最后一个成员未…...
vue基础
引入vue文件 <div id"app"><!--{{}}插值表达式,绑定vue中的data数据-->{{message}} </div><script src"vue.min.js"></script> <script>new Vue({el:#app,data:{message:Hello Vue}}) </script>单项…...

访问量突破1W,纪念一下~
Mr.kanglong, 继续加油!...
C# 处理TCP数据的类(服务端)
using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Threading;namespace TestDemo {/// <summary>/// 处理TCP数据的类(服务端)/// </summary>public class TcpService{/// <s…...

【Jenkins】调用API构建并钉钉通知
文章目录 Jenkins API介绍提交作业带参数的作业API 令牌 Shell调用代码 Jenkins API介绍 Jenkins 提供了远程访问 API。目前它有三种格式: XML JSON Python 远程访问 API 形式为"…/api/" 例如, Jenkins 安装位于https://ci.jenkins.io&a…...
Java NIO三大核心组件
文章目录 一、Buffer1、重要属性2、重要方法1)allocate()创建缓冲区2)put()写入到缓冲区3)flip()翻转4)get()从缓冲区读取5)rewind()倒带6)mark()和reset()7)clear()清空缓冲区8)使用…...
js数据排序方法(sort)?
在JavaScript中,可以使用Array的sort()方法对数据进行排序。下面是一个基本的例子,它展示了如何对一个数组进行升序和降序排序: // 创建一个数字数组 let numbers [2, 9, 1, 5, 8, 6];// 升序排序 let ascending numbers.sort(function(a,…...
若依框架学习笔记_mybatis
一、 在框架中引用的先后顺序 在ruoyi-system的resources下的xml中定义方法在java下的mapper包中引用方法在java下的service包中再引用mapper的方法 二、xml中的写法 标签: resultMap 返回数据sql 查询语句 可包含在其他操作中select 查询insert 插入update 更新…...

虚拟机的发展史:从分时系统到容器化
一、前世 早期计算机的价格非常昂贵,一台计算机可能需要花费几十万甚至上百万美元。例如,ENIAC计算机,作为世界上第一台通用电子数字计算机,当时的造价约为48万美元。科学家或者工程师们需要计算机的能力,但是买不起整…...

季涨约3~8%,DRAM合约价大幅回升 | 百能云芯
据TrendForce的研究显示,第4季DRAM与NAND Flash均价将开始全面上涨。特别是DRAM,预计第4季的合约价将季涨幅约在3%到8%之间。然而,这波上涨是否能持续,取决于供应商是否坚守减产策略以及实际需求的回升程度,尤其值得关…...
LocalDate的用法
日期时间转换 2023-03-30 14:25:00.000 DateTimeFormat(pattern "yyyy-MM-dd HH:mm:ss:sss")private LocalDateTime requestTimeStamp; 2021-06-18T10:46:19.67378508:00 new SimpleDateFormat("yyyy-MM-ddTHH:mm:ss:sssXXX");yyyy-mm-dd hh:mm:ss.sss 05…...
React通过ref获取子组件的数据和方法
父组件 1) ref必须传值, 否则childRef拿不到子组件的数据和方法 注意: 不一定使用app组件, 其他的任何父组件都可以 import "./App.css"; import React, { useEffect, useRef } from "react"; import RefToGetChild from "./components/RefToGetCh…...
Enhancing Self-Consistency and Performance of Pre-Trained Language Model
本文是LLM系列文章,针对《Enhancing Self-Consistency and Performance of Pre-Trained Language Models through Natural Language Inference》的翻译。 通过自然语言推理增强预训练语言模型的自一致性和性能 摘要1 引言2 相关工作3 通过关系检测进行一致性校正4 …...

龙虎榜——20250610
上证指数放量收阴线,个股多数下跌,盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型,指数短线有调整的需求,大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的:御银股份、雄帝科技 驱动…...

19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...