Telephony中ITelephony的AIDL调用关系

news2024/10/27 18:14:07

以Android14.0源码讲解

ITelephony来自framework下的com.android.internal.telephony包下

frameworks/base/telephony/java/com/android/internal/telephony/ITelephony.aidl

这个接口用于与Phone交互的界面,主要由TelephonyManager类使用,一些地方仍在直接使用它,如果可能的话,请清理它们,并使用TelephonyManager。意思就是不让你直接使用ITelephony 接口,要用里面的方法的话就使用TelephonyManager来获取。

 /**
94   * Interface used to interact with the phone.  Mostly this is used by the
95   * TelephonyManager class.  A few places are still using this directly.
96   * Please clean them up if possible and use TelephonyManager instead.
97   *
98   * {@hide}
99   */
100  interface ITelephony {

里面包含了384个方法,我们找一些常见的方法来学习。

1.通话相关

 /**
	     /*拨一个号码。这不会打电话。它显示拨号程序屏幕。*/
108      @UnsupportedAppUsage
109      void dial(String number);
110  
111      /**拨打指定号码。*/
116      @UnsupportedAppUsage
117      void call(String callingPackage, String number);
350  
351      /**返回特定Subscription的Call State。*/
354      int getCallStateForSubscription(int subId, String callingPackage, String featureId);

之前Android9.0还能 endCall(),answerRingingCall(),isRinging(String callingPackage)等可以控制通话的这些方法已经被移除了,只能获取通话的相关状态了。

在 Android 14 中,ITelephony 接口中的一些与呼叫控制相关的方法,如 endCall() 和 answerRingingCall(),被移除的原因主要是为了增强用户隐私和安全性,并遵循更严格的权限管理政策。以下是移除这些方法背后的主要原因:

  1. 用户隐私和安全
    Android 操作系统近年来越来越重视用户隐私和安全性。方法如 endCall() 和 answerRingingCall(),允许应用程序以编程方式挂断电话或接听来电,这涉及对电话呼叫进行直接控制。虽然这些方法过去在某些场景下(例如自动应答、自动挂断电话)很有用,但它们也存在潜在的隐私风险:

滥用风险:应用可以在用户不知情或未授权的情况下控制呼叫,导致隐私问题。例如,某些恶意应用可以滥用这些API来挂断重要的电话或在不需要用户参与的情况下接听电话。
侵犯用户选择:这些方法绕过了用户的决定,让应用能够控制电话呼叫流程,而不经过用户的同意或操作。
为了解决这些问题,Google 在 Android 14 中移除了这些容易被滥用的方法,进一步保护用户的隐私。

  1. 权限管理的演变
    随着 Android 的发展,Google 越来越强调权限的精细化管理,以确保用户对应用行为的更大控制。过去,某些方法可能只需要少量的权限(例如 READ_PHONE_STATE),但实际上,它们赋予应用极高的权力来控制用户的电话。

为了解决这种过度授权的问题,Google 逐步将与电话呼叫相关的操作限制给了系统应用或经过特别授权的应用,而普通第三方应用程序则无法再获得这些权限。

  1. 推动使用更安全的API
    Google 提供了更安全且受限的替代方案来处理呼叫控制功能。例如,通过官方的 TelecomManager API,应用程序可以注册为默认电话应用来控制电话呼叫。要成为默认电话应用,用户必须显式授予权限,这样能够更好地避免未经授权的操作。这种设计确保了只有用户明确选择的应用程序才能执行类似的操作,减少滥用的可能性。

例如:
TelecomManager API:可以用来控制电话呼叫,但仅限于注册为默认电话应用的程序,并且权限控制更严格。

TelecomManager telecomManager = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
telecomManager.endCall();  // 需要应用是默认电话应用
CallScreeningService:用于筛选和监控来电,同样受限于特定的系统权限。
  1. 限制低层次的接口调用
    ITelephony 是 Android 系统中的一个底层接口,通常不面向普通应用程序开发者,而是为系统应用或有特殊权限的应用设计的。过去,一些开发者通过反射等手段调用这些内部API,而这些API绕过了系统层级的限制和权限检查。在 Android 14 中,Google 通过移除这些方法,加强了对底层接口的封装,减少了未授权调用的风险。

  2. 提高系统稳定性
    随着 Android 系统的演变,Google 也希望简化并优化底层系统接口,以提高系统的稳定性和一致性。减少对底层 ITelephony API 的依赖,可以避免未来版本中出现的不兼容性问题,并减少系统维护的复杂度。

2.网络模式相关

553      /*返回subId的数据网络类型*/
559      int getDataNetworkTypeForSubscriber(int subId, String callingPackage,String callingFeatureId);

561      /** 返回subId的语音网络类型*/
569      int getVoiceNetworkTypeForSubscriber(int subId, String callingPackage,String callingFeatureId);

902       /*将网络选择模式设置为自动。*/
905      void setNetworkSelectionModeAutomatic(int subId);

		//让radio连接到输入网络,并将选择模式更改为手动。
 		boolean setNetworkSelectionModeManual(int subId, in OperatorInfo operatorInfo, boolean persisSelection);
 		
		//执行radio扫描并返回可用网络列表。也就是 获取搜网后的结果
 		CellNetworkScanResult getCellNetworkScanResults(int subId, String callingPackage,String callingFeatureId);
		
		//发起搜网请求
931      int requestNetworkScan(int subId, in boolean renounceFineLocationAccess,
932              in NetworkScanRequest request, in Messenger messenger, in IBinder binder,
933  	    in String callingPackage, String callingFeatureId);

		//停止搜网
941      void stopNetworkScan(int subId, int scanId);

设置网络模式(即,4G/3G/2G, 3G/2G, 2G only等) setPreferredNetworkType(int subId, int networkType);方法也移除了,普通应用不再能够直接更改这些设置。目前,只有具有 系统级权限 或 运营商特权 的应用程序才可能具有修改网络模式的权限。

3.手机相关

/*getDeviceId(String callingPackage);方法已经废弃使用getDeviceIdWithFeature代替,
返回Phone的唯一设备ID,例如IMEI GSM 和 CDMA phone的MEID。如果设备ID不可用,则返回null。*/
1317      String getDeviceIdWithFeature(String callingPackage, String callingFeatureId);

//通过slot 获取IMEI
1328      String getImeiForSlot(int slotIndex, String callingPackage, String callingFeatureId);

//返回给定插槽的MEID。
1356      String getMeidForSlot(int slotIndex, String callingPackage, String callingFeatureId);

// 获取设备软件版本号
1374      String getDeviceSoftwareVersionForSlot(int slotIndex, String callingPackage,
1375              String callingFeatureId);

IMEI:用于 GSM 和 LTE 网络的 15 位数字唯一标识符,广泛应用于全球。
MEID:主要用于 CDMA 网络的 14 位十六进制标识符。常见于美国和一些其他地区的移动通信网络。CDMA 网络由运营商如 Verizon(美国)或 KDDI(日本)使用。

在现代智能手机上,特别是双模设备(支持 GSM 和 CDMA 网络)的手机,可能同时有 IMEI 和 MEID 号码,分别用于不同的网络协议。随着 LTE 网络的发展,CDMA 技术的使用逐渐减少,因此 IMEI 逐渐成为全球统一的设备标识方法。

4.SIM卡相关

//提供一个pin来解锁特定subId的SIM卡。
163      boolean supplyPinForSubscriber(int subId, String pin);

//提供puk以解锁SIM卡,并将SIM卡pin设置为新pin。
173      boolean supplyPukForSubscriber(int subId, String puk, String pin);

// 判断是否插入了Sim卡
575      @UnsupportedAppUsage
576      boolean hasIccCard();

//获取默认eUICC卡的卡ID。如果没有eUICC,则返回{@link#INVALID_CARD_ID}。
1732      int getCardIdForDefaultEuicc(int subId, String callingPackage);

接下来看一下这些方法是如何实现的?

查看源码,PhoneInterfaceManager类继承了接口ITelephony.Stub ,相当于实现了ITelephony的所有方法。
在这里插入图片描述
先了解一下AIDL机制:

那为什么源码中PhoneInterfaceManager 可以 extends ITelephony.Stub 接口?

在 Java 和 Android 中,PhoneInterfaceManager 类通过 extends 继承 ITelephony.Stub 的情况,其实并不是简单的继承接口。这里涉及到了AIDL(Android Interface Definition Language)的特殊机制。

1. AIDL(Android Interface Definition Language)
在 Android 中,AIDL 是一种允许不同进程之间通信的机制,常用于实现跨进程调用(IPC)。当您定义一个 AIDL 接口时,系统会自动为您生成一个 Stub 类。这些生成的代码通常会继承 android.os.Binder 类,并实现您定义的接口方法。

例如,假设 ITelephony.aidl 是一个定义了远程接口的 AIDL 文件:

interface ITelephony {
    void setPreferredNetworkType(int type);
}
在编译时,Android SDK 会自动生成一个 ITelephony.Stub 类,这个类是 Binder 的子类,并且它实现了 ITelephony 接口。Stub 类有助于在服务器端处理来自客户端的远程方法调用。

2. ITelephony.Stub 是什么?
ITelephony.Stub 是 Android 自动生成的一个 抽象类,它既实现了 ITelephony 接口的所有抽象方法,又继承了 Binder 类。

ITelephony 接口:定义了远程接口的行为(如 setPreferredNetworkType())。
ITelephony.Stub 类:这个抽象类实现了 IBinder 接口,并负责处理客户端的远程调用请求。它还提供了方法用于将远程请求分发给实际的 PhoneInterfaceManager 实现。
简而言之,Stub 类是 Binder 的子类,封装了 AIDL 中的绑定逻辑,用来处理跨进程调用。

3. 为什么 PhoneInterfaceManager 可以 extends ITelephony.Stub?
由于 ITelephony.Stub 是一个抽象类,而不是一个接口,Java 中允许类继承另一个类。因此,PhoneInterfaceManager 继承 ITelephony.Stub 是合理的:

java
public class PhoneInterfaceManager extends ITelephony.Stub {
    @Override
    public void setPreferredNetworkType(int type) {
        // 具体的实现逻辑
    }
}
4. ITelephony.Stub 和 IPC 通信
PhoneInterfaceManager 继承 ITelephony.Stub 是为了处理来自其他进程的远程调用。由于 Android 系统中的很多服务(如电话、消息等)运行在单独的进程中,它们需要通过 AIDL 机制提供远程调用接口给其他进程使用。

通过继承 ITelephony.Stub,PhoneInterfaceManager 类能够作为服务端,处理来自客户端(如应用层)发起的远程调用请求。

5. 总结
PhoneInterfaceManager 继承 ITelephony.Stub,是因为 Stub 实际上是一个 AIDL 自动生成的 抽象类,它既实现了接口,又继承了 Binder 类,负责管理远程调用。
AIDL 机制允许跨进程调用,Stub 类帮助处理这些调用,因此 PhoneInterfaceManager 继承了 Stub,从而可以提供远程方法调用的服务。
这不是普通的类继承接口的情况,而是 AIDL 机制下的 自动生成类的继承。

看完小插曲,回到PhoneInterfaceManager类的学习,通过查看这个所在路径packages/services/Telephony/src/com/android/phone/PhoneInterfaceManager.java,发现它是运行在phone进程下的,它是PhoneGlobals在onCreate方法中初始化的 phoneMgr = PhoneInterfaceManager.init(this),并且这个只能初始化一次。

2439      /**
2440       * Initialize the singleton PhoneInterfaceManager instance.
2441       * This is only done once, at startup, from PhoneApp.onCreate().
2442       */
2443      /* package */ static PhoneInterfaceManager init(PhoneGlobals app) {
2444          synchronized (PhoneInterfaceManager.class) {
2445              if (sInstance == null) {
2446                  sInstance = new PhoneInterfaceManager(app);
2447              } else {
2448                  Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
2449              }
2450              return sInstance;
2451          }
2452      }
2453  
2454      /** Private constructor; @see init() */
2455      private PhoneInterfaceManager(PhoneGlobals app) {
2456          mApp = app; //得到PhoneGlobals的引用
2457          mCM = PhoneGlobals.getInstance().mCM; //CallManager引用
2458          mImsResolver = ImsResolver.getInstance(); //获取ImsResolver实例对象
2459          mSatelliteController = SatelliteController.getInstance();//获取SatelliteController实例对象
2460          mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE);
2461          mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);
2462          mMainThreadHandler = new MainThreadHandler(); //获取MainThreadHandler对象
2463          mTelephonySharedPreferences = PreferenceManager.getDefaultSharedPreferences(mApp);
2464          mNetworkScanRequestTracker = new NetworkScanRequestTracker();
2465          mPhoneConfigurationManager = PhoneConfigurationManager.getInstance();
2466          mRadioInterfaceCapabilities = RadioInterfaceCapabilityController.getInstance();
2467          mNotifyUserActivity = new AtomicBoolean(false);
2468          PropertyInvalidatedCache.invalidateCache(TelephonyManager.CACHE_KEY_PHONE_ACCOUNT_TO_SUBID);
2469          publish();
2470          CarrierAllowListInfo.loadInstance(mApp);
2471      }
	
7      private void publish() {
2488          if (DBG) log("publish: " + this);
2489  //将当前对象注册为 Telephony 服务,使其可以被系统访问和使用。ServiceManager.addService("phone", this);
2490          TelephonyFrameworkInitializer
2491                  .getTelephonyServiceManager()
2492                  .getTelephonyServiceRegisterer()
2493                  .register(this);
2494      }

先看getCallStateForSubscription方法是如何实现的?

3219      @Override
3220      public int getCallStateForSubscription(int subId, String callingPackage, String featureId) {
			//判断权限
3221          if (CompatChanges.isChangeEnabled(
3222                  TelecomManager.ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION,
3223                  Binder.getCallingUid())) {
3224              // Check READ_PHONE_STATE for API version 31+
3225              if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, subId, callingPackage,
3226                      featureId, "getCallStateForSubscription")) {
3227                  throw new SecurityException("getCallState requires READ_PHONE_STATE for apps "
3228                          + "targeting API level 31+.");
3229              }
3230          }
3231          final long identity = Binder.clearCallingIdentity();
3232          try {
3233              Phone phone = getPhone(subId);	
				//返回CALL_STATE状态
3234              return phone == null ? TelephonyManager.CALL_STATE_IDLE :
3235                      PhoneConstantConversions.convertCallState(phone.getState());
3236          } finally {
3237              Binder.restoreCallingIdentity(identity);
3238          }
3239      }

把TelephonyManager.CALL_STATE_*转成PhoneConstants.State

frameworks/base/telephony/java/com/android/internal/telephony/PhoneConstantConversions.java

39      /**
40       * Convert the TelephonyManager.CALL_STATE_* constants into the
41       * {@link PhoneConstants.State} enum for the public API.
42       */
43      public static PhoneConstants.State convertCallState(int state) {
44          switch (state) {
45              case TelephonyManager.CALL_STATE_RINGING:
46                  return PhoneConstants.State.RINGING;
47              case TelephonyManager.CALL_STATE_OFFHOOK:
48                  return PhoneConstants.State.OFFHOOK;
49              default:
50                  return PhoneConstants.State.IDLE;
51          }
52      }
53  

再来看一个getDataNetworkTypeForSubscriber方法

5361      @Override
5362      public int getDataNetworkTypeForSubscriber(int subId, String callingPackage,
5363              String callingFeatureId) {
5364          String functionName = "getDataNetworkTypeForSubscriber";
5365          if (!TelephonyPermissions.checkCallingOrSelfReadNonDangerousPhoneStateNoThrow(
5366                  mApp, functionName)) {
5367              if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
5368                      mApp, subId, callingPackage, callingFeatureId, functionName)) {
5369                  return TelephonyManager.NETWORK_TYPE_UNKNOWN;
5370              }
5371          }
5372  
5373          final long identity = Binder.clearCallingIdentity();
5374          try {
5375              final Phone phone = getPhone(subId);
				//也是通过phone对象来获取DataNetworkType
5376              if (phone != null) {
5377                  return phone.getServiceState().getDataNetworkType();
5378              } else {
5379                  return TelephonyManager.NETWORK_TYPE_UNKNOWN;
5380              }
5381          } finally {
5382              Binder.restoreCallingIdentity(identity);
5383          }
5384      }

看了两个方法,可以发现在PhoneInterfaceManager类中,就是通过获取phone对象并调用其方法来实现具体功能的。

既然PhoneInterfaceManager类是作为服务端的,那么客户端是怎么来获取这里面的方法呢,App的开发过程中是没办法获取到ITelephony接口的。可以通过 frameworks/base/telephony/java/android/telephony/TelephonyManager.java类来获取响应的方法,在TelephonyManager类中,ITelephony通过ITelephony.Stub.asInterface获取ITelephony类。
在这里插入图片描述

然后在telephony对象中获取getDataNetworkTypeForSubscriber方法,相当于远程调用了PhoneInterfaceManager中的getDataNetworkTypeForSubscriber()方法

6      public int getDataNetworkType(int subId) {
3147          try{
				//调用getITelephony()方法得到ITelephony的实例对象
3148              ITelephony telephony = getITelephony();
3149              if (telephony != null) {
			// 调用ITelephony实例对象的getDataNetworkTypeForSubscriber()方法
            // 实际上是远程调用了PhoneInterfaceManager中的getDataNetworkTypeForSubscriber()方法
3150                  return telephony.getDataNetworkTypeForSubscriber(subId, getOpPackageName(),
3151                          getAttributionTag());
3152              } else {
3153                  // This can happen when the ITelephony interface is not up yet.
3154                  return NETWORK_TYPE_UNKNOWN;
3155              }
3156          } catch(RemoteException ex) {
3157              // This shouldn't happen in the normal case
3158              return NETWORK_TYPE_UNKNOWN;
3159          } catch (NullPointerException ex) {
3160              // This could happen before phone restarts due to crashing
3161              return NETWORK_TYPE_UNKNOWN;
3162          }
3163      }

至此就完成了,但是getDataNetworkTypeForSubscriber方法中返回的这个数据phone.getServiceState().getDataNetworkType(),其实要深究的话,就要到ServiceState类了

/frameworks/base/telephony/java/android/telephony/ServiceState.java

1770      public @NetworkType int getDataNetworkType() {
1771          final NetworkRegistrationInfo iwlanRegInfo = getNetworkRegistrationInfo(
1772                  NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
1773          final NetworkRegistrationInfo wwanRegInfo = getNetworkRegistrationInfo(
1774                  NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
1775  
1776          // For legacy mode device, or AP-assisted mode device but IWLAN is out of service, use
1777          // the RAT from cellular.
1778          if (iwlanRegInfo == null || !iwlanRegInfo.isInService()) {
1779              return (wwanRegInfo != null) ? wwanRegInfo.getAccessNetworkTechnology()
1780                      : TelephonyManager.NETWORK_TYPE_UNKNOWN;
1781          }
1782  
1783          // At this point, it must be an AP-assisted mode device and IWLAN is in service. We should
1784          // use the RAT from IWLAN service is cellular is out of service, or when both are in service
1785          // and any APN type of data is preferred on IWLAN.
1786          if (!wwanRegInfo.isInService() || mIsIwlanPreferred) {
1787              return iwlanRegInfo.getAccessNetworkTechnology();
1788          }
1789  
1790          // If both cellular and IWLAN are in service, but no APN is preferred on IWLAN, still use
1791          // the RAT from cellular.
1792          return wwanRegInfo.getAccessNetworkTechnology();
1793      }

它们的调用关系:
1> App获取TelephonyManger的实例对象
2>然后调用TelephonyManger的getDataNetworkType()
3>在方法中通过getITelephony()获取ITelephony 实例
4>ITelephony 跨进程 调用PhoneInterfaceManager类中的getDataNetworkTypeForSubscriber
5>而getDataNetworkTypeForSubscriber方法中又通过phone对象调用ServiceState返回DataNetworkType

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2224844.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

开拓鸿蒙测试新境界,龙测科技引领自动化测试未来

在当今科技舞台上,鸿蒙 OS 以非凡先进性强势登场,打破传统操作系统格局,为软件测试领域带来全新机遇与艰巨挑战。 一、鸿蒙 OS 的辉煌崛起 (一)壮丽发展历程与卓越市场地位 鸿蒙 OS 的发展如波澜壮阔的史诗。2023 年…

音视频开发之旅(98) -潜扩散模型(Latent Diffusion Model)原理及源码解析

目录 1.背景 2. 潜扩散模型(Latent Diffusion Model)原理 3. 应用场景 4. 推理源码解析 5. 资料 一、背景 前面我们分析扩散模型(Diffusion Model)了解到,它通过向数据中添加噪声,然后训练一个去噪模…

SSM学习day01 JS基础语法

一、JS基础语法 跟java有点像,但是不用注明数据类型 使用var去声明变量 特点1:var关键字声明变量,是为全局变量,作用域很大。在一个代码块中定义的变量,在其他代码块里也能使用 特点2:可以重复定义&#…

【mysql进阶】4-6. InnoDB 磁盘文件

InnoDB 磁盘⽂件 1 InnoDB存储引擎包含哪些磁盘⽂件? 🔍 分析过程 ✅ 解答问题 InnoDB的磁盘⽂件主要是表空间⽂件和其他⽂件,表空间包括:系统表空间、独⽴表空间、通⽤表空间、临时表空间和撤销表空间;其他⽂件有重做…

XQT_UI 组件|03 |加载组件 XQtLoading

XQtLoading 使用文档 简介 XQtLoading 是一个自定义的加载动画组件,旨在为用户提供可配置的旋转花瓣动画效果。它可以在应用程序中用于指示加载状态,提升用户体验。 特征 可配置性:用户可以根据需求调整旋转周期、缩放周期、最大/最小缩放…

JavaScript part2

一.前言 前面我们讲了一下js的基础语法,但是这些还是远远不够的,我们要想操作标签,实现一个动态且好看的页面,就得学会BOM和DOM,这些都是浏览器和页面的,这样我们才能实现一个好看的页面 二.BOM对象 BOM…

golang将指针传给cgo后还能被回收吗?

问题&#xff1a; 如果把golang分配的变量&#xff0c;其指针通过cgo传给c&#xff0c;并被c存储&#xff0c;那这个变量还能被gc回收吗&#xff1f; 实验代码&#xff1a; test_memory_leak.go package main/* #include <stdlib.h> #include <string.h> #incl…

yolov11的onnx模型C++ 调用

yolov11的onnx模型C调用 效果图一、python调用二、onnx模型导出三、python的onnx调用调用检测模型调用分割模型 四、C的onnx模型调用五 、视频流的检测后续 效果图 一、python调用 本文只记录生成的yolov11模型如何调用&#xff0c;其他可参考各种yolov11博客 模型下载&#x…

Spring Boot 应用开发全攻略:从入门到精通

Spring Boot 应用开发全攻略&#xff1a;从入门到精通 引言 在当今快速发展的软件开发领域&#xff0c;Spring Boot 作为一种快速开发框架&#xff0c;凭借其简洁、易用的特性&#xff0c;赢得了开发者的广泛青睐。无论是微服务架构还是传统的单体应用&#xff0c;Spring Boo…

Redis 单机、主从、哨兵和集群架构详解和搭建

目录 前言 单机部署 检查安装 gcc 环境 下载安装 Redis 启动 Redis 关闭 Redis 配置Redis 主从部署 整体架构图 主从复制配置 重启 Redis 验证 主从复制的作⽤ 主从复制缺点 哨兵部署&#xff08;Sentinel&#xff09; 整体架构图 哨兵模式配置 启动哨兵 验证…

首席数据官和首席数据分析官

根据分析人士的预测&#xff0c;首席数据官&#xff08;CDO&#xff09;和首席数据分析官&#xff08;CDAO&#xff09;必须更有效地展示他们对企业和AI项目的价值&#xff0c;以保障其在高管层的地位。Gartner的最新报告指出&#xff0c;CDO和CDAO在AI时代需要重新塑造自身定位…

ElegantBook:优美的 LATEX 书籍模板(中文的latex模版)

关注B站可以观看更多实战教学视频&#xff1a;hallo128的个人空间 ElegantBook&#xff1a;优美的 LATEX 书籍模板&#xff08;中文的latex模版&#xff09; Github地址&#xff1a;https://github.com/ElegantLaTeX/ElegantBook使用说明文档&#xff1a;https://static.latexs…

C++11实践指北

C11&#xff1a;书、在线工具、库。 书 1. 《现代C语言核心特性解析》 覆盖 C11~C20 特性的讲解。 视频跟读&#xff1a;https://www.bilibili.com/video/BV1nN4y1j7fv 现代CPP随笔_0CCh - 每天5分钟了解现代C新特性 2. 《C Primer》第五版 基于 C11 的 C 入门书。 正在看…

故障诊断 | CNN-ResNets滚动轴承故障诊断实例代码

故障诊断 | CNN-ResNets滚动轴承故障诊断实例代码 目录 故障诊断 | CNN-ResNets滚动轴承故障诊断实例代码效果一览基本介绍程序设计参考资料 效果一览 基本介绍 CNN-ResNets&#xff08;卷积神经网络-残差网络&#xff09;在滚动轴承故障诊断中是一种常用的方法。这种方法利用…

使用Angular构建动态Web应用

&#x1f496; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4bb; Gitee主页&#xff1a;瑕疵的gitee主页 &#x1f680; 文章专栏&#xff1a;《热点资讯》 使用Angular构建动态Web应用 1 引言 2 Angular简介 3 安装Angular 4 创建Angular项目 5 设计应用结构 6 创建组件 7 …

Python小游戏14——雷霆战机

首先&#xff0c;你需要确保安装了Pygame库。如果你还没有安装&#xff0c;可以使用pip来安装&#xff1a; bash pip install pygame 代码如下&#xff1a; python import pygame import sys import random # 初始化Pygame pygame.init() # 设置屏幕大小 screen_width 800 scr…

云原生笔记

#1024程序员节|征文# 单页应用(Single-Page Application&#xff0c;SPA) 云原生基础 云原生全景内容宽泛&#xff0c;以至于刚开始就极具挑战性。 云原生应用是高度分布式系统&#xff0c;它们存在于云中&#xff0c;并且能够对变化保持韧性。系统是由多个服务组成的&#…

java-JVM面试问题-2024

1、简单介绍下虚拟机内存模型&#xff1f; VM由三部分组成&#xff1a;类加载子系统、运行时数据区、执行引擎 类加载子系统&#xff1a;通过类加载机制加载类的class文件&#xff0c;如果该类是第一次加载&#xff0c;会执行加载、验证、解析。只负责class文件的加载&#x…

基于neo4j的医疗问诊系统

当你身体不适时&#xff0c;想要找到准确的答案却经常遇到模棱两可的答复&#xff0c;糟心吗&#xff1f;现在&#xff0c;基于neo4j的智能医疗问诊系统为你带来全新体验&#xff01;我们设计了一个具备自动化问答功能的医疗系统&#xff0c;帮助用户快速获取专业的健康知识答案…

如何具备阅读JAVA JDK虚拟机源码能力

源码位置https://github.com/openjdk/jdk 核心实现源码[部分截图] /* * Copyright (c) 1995, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistr…