高通Android 11/12/13 通過包名設置默認launcher

背景:最近在封裝供第三應用系統SDK 接口,遇到一個無法通過包名設置主launcher代碼坑所以記錄下。
?

涉及類roles.xml #?<!---~ @see com.android.settings.applications.defaultapps.DefaultHomePreferenceController~ @see com.android.settings.applications.defaultapps.DefaultHomePicker~ @see com.android.server.pm.PackageManagerService#setHomeActivity(ComponentName, int)-->DeaultAppActivity.java#onCreateDefaultAppChildFragment.java #?onRoleChanged # addPreferenceAutoDefaultAppListFragment#onActivityCreatedManageRoleHolderStateLiveData.java #setRoleHolderAsUserHandheldDefaultAppFragment.java(packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/role/ui/handheld)#newInstanceRoleManager.java #addRoleHolderAsUserIRoleManager.aidl #addRoleHolderAsUserRole.java? #getDefaultHolders?TwoTargetPreference.java?#OnSecondTargetClickListenerPackageManagerShellCommand.java#runSetHomeActivityResolverActivity.java # onCreateParseActivityUtils #private static ParseResult<ParsedActivity> parseActivityOrAlias(ParsedActivity activity,ParsingPackage pkg, String tag, XmlResourceParser parser, Resources resources,TypedArray array, boolean isReceiver, boolean isAlias, boolean visibleToEphemeral,ParseInput input, int parentActivityNameAttr, int permissionAttr,int exportedAttr)

1、一開始我是這樣寫的,代碼如下圖所示。

private void setDefaultLauncher3(Context context,String packageName,String className) {try {PackageManager pm = getPackageManager();Log.i("deflauncher", "deflauncher : PackageName = " + packageName + " ClassName = " + className);IntentFilter filter = new IntentFilter();filter.addAction("android.intent.action.MAIN");filter.addCategory("android.intent.category.HOME");filter.addCategory("android.intent.category.DEFAULT");Intent intent = new Intent(Intent.ACTION_MAIN);intent.addCategory(Intent.CATEGORY_HOME);List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);final int N = list.size();ComponentName[] set = new ComponentName[N];int bestMatch = 0;for (int i = 0; i < N; i++) {ResolveInfo r = list.get(i);set[i] = new ComponentName(r.activityInfo.packageName,r.activityInfo.name);if (r.match > bestMatch) bestMatch = r.match;}ComponentName preActivity = new ComponentName(packageName, className);pm.addPreferredActivity(filter, bestMatch, set, preActivity);} catch (Exception e) {e.printStackTrace();}}

2、具體添加位置參考在frameworks/base/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java

3、寫死launcher包名主activity類名方法如下代碼所示?app.olauncher.MainActivity

/*** This method shares parsing logic between Activity/Receiver/alias instances, but requires* passing in booleans for isReceiver/isAlias, since there's no indicator in the other* parameters.** They're used to filter the parsed tags and their behavior. This makes the method rather* messy, but it's more maintainable than writing 3 separate methods for essentially the same* type of logic.*/@NonNullprivate static ParseResult<ParsedActivity> parseActivityOrAlias(ParsedActivity activity,ParsingPackage pkg, String tag, XmlResourceParser parser, Resources resources,TypedArray array, boolean isReceiver, boolean isAlias, boolean visibleToEphemeral,ParseInput input, int parentActivityNameAttr, int permissionAttr,int exportedAttr) throws IOException, XmlPullParserException {String parentActivityName = array.getNonConfigurationString(parentActivityNameAttr, Configuration.NATIVE_CONFIG_VERSION);if (parentActivityName != null) {String packageName = pkg.getPackageName();String parentClassName = ParsingUtils.buildClassName(packageName, parentActivityName);if (parentClassName == null) {Log.e(TAG, "Activity " + activity.getName()+ " specified invalid parentActivityName " + parentActivityName);} else {activity.setParentActivity(parentClassName);}}String permission = array.getNonConfigurationString(permissionAttr, 0);if (isAlias) {// An alias will override permissions to allow referencing an Activity through its alias// without needing the original permission. If an alias needs the same permission,// it must be re-declared.activity.setPermission(permission);} else {activity.setPermission(permission != null ? permission : pkg.getPermission());}final boolean setExported = array.hasValue(exportedAttr);if (setExported) {activity.exported = array.getBoolean(exportedAttr, false);}final int depth = parser.getDepth();int type;while ((type = parser.next()) != XmlPullParser.END_DOCUMENT&& (type != XmlPullParser.END_TAG|| parser.getDepth() > depth)) {if (type != XmlPullParser.START_TAG) {continue;}final ParseResult result;if (parser.getName().equals("intent-filter")) {ParseResult<ParsedIntentInfo> intentResult = parseIntentFilter(pkg, activity,!isReceiver, visibleToEphemeral, resources, parser, input);if (intentResult.isSuccess()) {ParsedIntentInfo intent = intentResult.getResult();if (intent != null) {Log.e(TAG,"ZM activityName="+activity.getName());if("app.olauncher.MainActivity".equals(activity.getName())){intent.addCategory("android.intent.category.HOME");intent.addCategory("android.intent.category.DEFAULT");intent.setPriority(1000);}activity.order = Math.max(intent.getOrder(), activity.order);         activity.addIntent(intent);if (LOG_UNSAFE_BROADCASTS && isReceiver&& pkg.getTargetSdkVersion() >= Build.VERSION_CODES.O) {int actionCount = intent.countActions();for (int i = 0; i < actionCount; i++) {final String action = intent.getAction(i);if (action == null || !action.startsWith("android.")) {continue;}if (!SAFE_BROADCASTS.contains(action)) {Slog.w(TAG,"Broadcast " + action + " may never be delivered to "+ pkg.getPackageName() + " as requested at: "+ parser.getPositionDescription());}}}}}result = intentResult;} else if (parser.getName().equals("meta-data")) {result = ParsedComponentUtils.addMetaData(activity, pkg, resources, parser, input);} else if (parser.getName().equals("property")) {result = ParsedComponentUtils.addProperty(activity, pkg, resources, parser, input);} else if (!isReceiver && !isAlias && parser.getName().equals("preferred")) {ParseResult<ParsedIntentInfo> intentResult = parseIntentFilter(pkg, activity,true /*allowImplicitEphemeralVisibility*/, visibleToEphemeral,resources, parser, input);if (intentResult.isSuccess()) {ParsedIntentInfo intent = intentResult.getResult();if (intent != null) {pkg.addPreferredActivityFilter(activity.getClassName(), intent);}}result = intentResult;} else if (!isReceiver && !isAlias && parser.getName().equals("layout")) {ParseResult<ActivityInfo.WindowLayout> layoutResult =parseActivityWindowLayout(resources, parser, input);if (layoutResult.isSuccess()) {activity.windowLayout = layoutResult.getResult();}result = layoutResult;} else {result = ParsingUtils.unknownTag(tag, pkg, parser, input);}if (result.isError()) {return input.error(result);}}if (!isAlias && activity.launchMode != LAUNCH_SINGLE_INSTANCE_PER_TASK&& activity.metaData != null && activity.metaData.containsKey(ParsingPackageUtils.METADATA_ACTIVITY_LAUNCH_MODE)) {final String launchMode = activity.metaData.getString(ParsingPackageUtils.METADATA_ACTIVITY_LAUNCH_MODE);if (launchMode != null && launchMode.equals("singleInstancePerTask")) {activity.launchMode = LAUNCH_SINGLE_INSTANCE_PER_TASK;}}ParseResult<ActivityInfo.WindowLayout> layoutResult =resolveActivityWindowLayout(activity, input);if (layoutResult.isError()) {return input.error(layoutResult);}activity.windowLayout = layoutResult.getResult();if (!setExported) {boolean hasIntentFilters = activity.getIntents().size() > 0;if (hasIntentFilters) {final ParseResult exportedCheckResult = input.deferError(activity.getName() + ": Targeting S+ (version " + Build.VERSION_CODES.S+ " and above) requires that an explicit value for android:exported be"+ " defined when intent filters are present",DeferredError.MISSING_EXPORTED_FLAG);if (exportedCheckResult.isError()) {return input.error(exportedCheckResult);}}activity.exported = hasIntentFilters;}return input.success(activity);}

4、在ResolverActivity.java 中onCreate方法中 執行以下代碼,代碼路徑?/frameworks/base/core/java/com/android/internal/app/ResolverActivity.java

protected void onCreate(Bundle savedInstanceState, Intent intent,CharSequence title, int defaultTitleRes, Intent[] initialIntents,List<ResolveInfo> rList, boolean supportsAlwaysUseOption) {setTheme(appliedThemeResId());super.onCreate(savedInstanceState);if (mResolvingHome) {setDefaultLauncher3();finish();return;}

5、靈活一點如果動態設置launcher流程又不一樣,下圖是Setttings默認主屏幕應用? launcher列表選項(這個界面radiobutton控件通過preference動態添加?這個addPreference(preference):

6、點擊事件位置代碼路徑packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java

 private void addPreference(@NonNull String key, @NonNull Drawable icon,@NonNull CharSequence title, boolean checked, @Nullable ApplicationInfo applicationInfo,@NonNull ArrayMap<String, Preference> oldPreferences,@NonNull PreferenceScreen preferenceScreen, @NonNull Context context) {TwoStatePreference preference = (TwoStatePreference) oldPreferences.get(key);if (preference == null) {preference = requirePreferenceFragment().createApplicationPreference(context);preference.setKey(key);preference.setIcon(icon);preference.setTitle(title);preference.setPersistent(false);preference.setOnPreferenceChangeListener((preference2, newValue) -> false);preference.setOnPreferenceClickListener(this);}Log.e("DefaultAppChildFragment","addPreference");preference.setChecked(checked);if (applicationInfo != null) {mRole.prepareApplicationPreferenceAsUser(preference, applicationInfo, mUser, context);}preferenceScreen.addPreference(preference);}

logcat日志

 DefaultAppChildFragment com.android.permissioncontroller     E  addPreference

7、另外一種通過指令去設置?adb shell pm set-home-activity ?app.olauncher.debug (主launcher包名),驗證過是沒問題的。

8、實際調用還是通過RoleManager#addRoleHolderAsUser方法去添加為主Launcher

代碼路徑packages\modules\Permission\framework-s\java\android\app\role\RoleManager.java

  /*** Add a specific application to the holders of a role. If the role is exclusive, the previous* holder will be replaced.* <p>* <strong>Note:</strong> Using this API requires holding* {@code android.permission.MANAGE_ROLE_HOLDERS} and if the user id is not the current user* {@code android.permission.INTERACT_ACROSS_USERS_FULL}.** @param roleName the name of the role to add the role holder for* @param packageName the package name of the application to add to the role holders* @param flags optional behavior flags* @param user the user to add the role holder for* @param executor the {@code Executor} to run the callback on.* @param callback the callback for whether this call is successful** @see #getRoleHoldersAsUser(String, UserHandle)* @see #removeRoleHolderAsUser(String, String, int, UserHandle, Executor, Consumer)* @see #clearRoleHoldersAsUser(String, int, UserHandle, Executor, Consumer)** @hide*/@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)@SystemApipublic void addRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,@ManageHoldersFlags int flags, @NonNull UserHandle user,@CallbackExecutor @NonNull Executor executor, @NonNull Consumer<Boolean> callback) {Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");Objects.requireNonNull(user, "user cannot be null");Objects.requireNonNull(executor, "executor cannot be null");Objects.requireNonNull(callback, "callback cannot be null");try {mService.addRoleHolderAsUser(roleName, packageName, flags, user.getIdentifier(),createRemoteCallback(executor, callback));} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}

打印logcat日志如下所示

2024-05-14 01:18:43.314  1653-1653  RoleManager             pid-1653                             D  Package added as role holder, role: android.app.role.HOME, package: com.android.launcher3
2024-05-14 01:47:11.673  2854-23939 RoleContro...erviceImpl com.android.permissioncontroller     I  Package is already a role holder, package: com.android.launcher3, role: android.app.role.HOME
2024-05-14 01:47:11.674  1653-1653  RoleManager             pid-1653                             D  Package added as role holder, role: android.app.role.HOME, package: com.android.launcher32024-05-14 01:18:43.319  2854-2854  DefaultApp...ragment ZM com.android.permissioncontroller     E  key=app.olauncher.debugtitle=Olauncher
2024-05-14 01:18:43.324  2854-2854  DefaultApp...ragment ZM com.android.permissioncontroller     E  key=com.android.launcher3title=Quickstep
2024-05-14 01:18:43.332  2854-2854  DefaultApp...ragment ZM com.android.permissioncontroller     E  key=app.olauncher.debugtitle=Olauncher
2024-05-14 01:18:43.338  2854-2854  DefaultApp...ragment ZM com.android.permissioncontroller     E  key=com.android.launcher3title=Quickstep
2024-05-14 01:47:10.880  2854-2854  DefaultApp...ragment ZM com.android.permissioncontroller     E  key=app.olauncher.debugtitle=Olauncher
2024-05-14 01:47:10.885  2854-2854  DefaultApp...ragment ZM com.android.permissioncontroller     E  key=com.android.launcher3title=Quickstep

9、代碼路徑 packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/role/ui/ManageRoleHolderStateLiveData.java

10、代碼路徑frameworks\base\services\core\java\com/android\server\pm\PackageManagerShellCommand.java

private int runSetHomeActivity() {final PrintWriter pw = getOutPrintWriter();int userId = UserHandle.USER_SYSTEM;String opt;while ((opt = getNextOption()) != null) {switch (opt) {case "--user":userId = UserHandle.parseUserArg(getNextArgRequired());break;default:pw.println("Error: Unknown option: " + opt);return 1;}}String pkgName;String component = getNextArg();if (component.indexOf('/') < 0) {// No component specified, so assume it's just a package name.pkgName = component;} else {ComponentName componentName =component != null ? ComponentName.unflattenFromString(component) : null;if (componentName == null) {pw.println("Error: invalid component name");return 1;}pkgName = componentName.getPackageName();}final int translatedUserId =translateUserId(userId, UserHandle.USER_NULL, "runSetHomeActivity");final CompletableFuture<Boolean> future = new CompletableFuture<>();try {RoleManager roleManager = mContext.getSystemService(RoleManager.class);roleManager.addRoleHolderAsUser(RoleManager.ROLE_HOME, pkgName, 0,UserHandle.of(translatedUserId), FgThread.getExecutor(), future::complete);boolean success = future.get();if (success) {pw.println("Success");return 0;} else {pw.println("Error: Failed to set default home.");return 1;}} catch (Exception e) {pw.println(e.toString());return 1;}}

11、最后可以把這些代碼添加自己自定義系統服務AIDL接口 ,然后在Android.bp中添加源碼編譯路徑(不知道怎么添加AIDL源碼編譯路徑看我之前這篇文章高通 Android 12 源碼編譯aidl接口_安卓12 怎么寫aidl-CSDN博客)

12、在自己app應用調用通過 如下代碼 進行設置即可(Process導入android.os包切記哈)

/*** 設置當前Launcher** @param packageName 傳入第三方launcher包名*/public void setCurrentLauncher(String packageName) {setRoleHolderAsUser(RoleManager.ROLE_HOME, packageName, 0, Process.myUserHandle(), mContext);}

13、最后別忘記如果你是app調用代碼的時候記得加系統簽名哈 AndroidManifest.xml中 ,否則也不會生效。

 android:sharedUserId="android.uid.system"

到這里基本結束了,轉載請注明出處高通Android 11/12/13 通過包名設置默認launcher-CSDN博客,謝謝!

感謝

Android R設置默認桌面_setroleholderasuser-CSDN博客

Android10.0(Q) 默認應用設置(電話、短信、瀏覽器、主屏幕應用)_android.app.role.browser-CSDN博客

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/12684.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/12684.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/12684.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

重啟服務器后node節點顯示NotReady

場景&#xff1a;夜間進行了斷電維護&#xff0c;重啟后發現業務無法使用&#xff0c;檢查發現一個node節點顯示NotReady. 去到目標服務器查看kubelet服務未成功啟動 journalctl -u kubelet 執行journalctl -u kubelet 查看日志發現提示&#xff1a; ailed to run Kubelet: run…

BFS和DFS優先搜索算法

1. BFS與DFS 1.1 BFS DFS即Depth First Search&#xff0c;深度優先搜索。它是一種圖遍歷算法&#xff0c;它從一個起始點開始&#xff0c;逐層擴展搜索范圍&#xff0c;直到找到目標節點為止。 這種算法通常用于解決“最短路徑”問題&#xff0c;比如在迷宮中找到從起點到終…

鐵路機輛作業移動智能終端的特點是什么?

在鐵路機輛作業的現代化進程中&#xff0c;移動智能終端以其獨特的優勢成為了不可或缺的裝備。這些終端以其高度的便攜性&#xff0c;使得工作人員能夠隨時隨地處理各種作業任務&#xff0c;極大地提升了工作效率。它們具備出色的抗干擾性和高防護性&#xff0c;能夠在復雜多變…

算法學習系列(六十一):樹形DP

目錄 引言一、沒有上司的舞會二、樹的重心三、樹的最長路徑四、樹的中心 引言 關于這個樹形 D P DP DP 代碼其實都是那一套&#xff0c;核心還是在于思維上的難度&#xff0c;關鍵是這個思路你能不能想明白&#xff0c;想明白了就非常的簡單&#xff0c;因為代碼幾乎長得都差…

LLM應用-prompt提示:讓大模型總結生成思維導圖

第一步&#xff1a;大模型生成markdown思維導圖格式 例如&#xff1a;kimi 總結pdf文檔案例&#xff1a; 生成的markdown格式&#xff1a; # 知識圖譜的構建及應用 ## 一、知識圖譜的構建 ### 1. 數據采集 - 來源&#xff1a;結構化數據庫、半結構化網頁、非結構化文本 - 預處…

React useState 的調用規則與最佳實踐:為何不在條件語句內使用 useState

在React中&#xff0c;useState 的調用確實有一些特定的規則和最佳實踐 以下是為什么通常不推薦在 if 語句內調用 useState 的原因&#xff1a; 1、Hooks 規則&#xff1a; React Hooks 的規則之一是&#xff0c;你應該在函數組件的頂層調用它們&#xff0c;而不是在循環、條…

技術管理者如何建立權威?

很多技術管理者經常抱怨管理不好做&#xff0c;還是做技術容易&#xff0c;完全受自己控制。員工一點都不聽自己的&#xff0c;安排的工作拖拖拉拉&#xff0c;一點執行力都沒有。 不是管理難做&#xff0c;而是管理者沒有建立權威。如何建立權威&#xff0c;參考以下四點。 …

PCIE V3.0物理層協議學習筆記

一、說明 PCI-Express(peripheral component interconnect express)是一種高速串行計算機擴展總線標準&#xff0c;它原來的名稱為“3GIO”&#xff0c;是由英特爾在2001年提出的&#xff0c;旨在替代舊的PCI&#xff0c;PCI-X和AGP總線標準。 PCIe屬于高速串行點對點雙通道高…

8.11 矢量圖層線要素單一符號使用二

文章目錄 前言箭頭&#xff08;Arrow&#xff09;QGis設置線符號為箭頭(Arrow)二次開發代碼實現 總結 前言 本章介紹矢量圖層線要素單一符號中箭頭&#xff08;Arrow&#xff09;的使用說明&#xff1a;文章中的示例代碼均來自開源項目qgis_cpp_api_apps 箭頭&#xff08;Arr…

證照之星是什么軟件 證照之星哪個版本好用?證照之星支持哪些相機 證照之星XE免費版

許多人都需要使用證件照&#xff0c;為了滿足這一需求&#xff0c;人們會使用照相機、手機、電腦等工具進行拍攝。除此之外&#xff0c;市面上還存在專門的證件照拍攝軟件&#xff0c;比如證照之星。那么&#xff0c;各位小伙伴是否了解證照之星哪個版本好用&#xff0c;證照之…

如何利用3D可視化大屏提升信息展示效果?

老子云3D可視化平臺https://www.laozicloud.com/ 引言 在信息爆炸的時代&#xff0c;如何有效地傳達和展示信息成為了各行各業的一大挑戰。傳統的平面展示方式已經無法滿足人們對信息展示的需求&#xff0c;3D可視化大屏應運而生&#xff0c;成為了提升信息展示效果的利器。本…

會員管理系統應該具備哪些功能?

?會員管理系統應該具備一系列核心功能&#xff0c;以滿足企業在會員管理、營銷和客戶服務等方面的需求。 以下是一些關鍵的會員管理系統功能&#xff1a; 1、會員信息管理&#xff1a;這是會員管理系統的基本功能&#xff0c;包括會員注冊、信息錄入、修改和查詢等。系統應支…

URL入參出參請求頭可配置化

整體思路 通過spring的Spell表達式解析變量的參數值&#xff0c;參數名定義為${XXX},在解析參數值后&#xff0c;將${XXX}替換成#XXX以匹配Spell表達式。 核心實現類 package com.example.spring_boot_study.spring.spell;import cn.hutool.core.map.MapUtil; import cn.hut…

大模型相關內容的研究學習

大模型研究學習 1.大模型的“幻覺” 幻覺可以分為事實性幻覺和忠實性幻覺。 事實性幻覺&#xff0c;是指模型生成的內容與可驗證的現實世界事實不一致。 比如問模型“第一個在月球上行走的人是誰&#xff1f;”&#xff0c;模型回復“Charles Lindbergh在1951年月球先驅任務…

the7主題下載,探索WordPress主題的無限可能

在數字時代&#xff0c;一個出色的網站是任何企業或個人品牌的必備。但在這個競爭激烈的網絡世界中&#xff0c;如何讓您的網站脫穎而出&#xff1f;答案就是 the7 —— 一款專為創造獨特和視覺沖擊力強的網站而設計的 WordPress 主題。 1. 無限設計可能性 the7 以其獨特的設…

探索政務熱線24小時在線服務:提升政府服務效能與民眾滿意度

一. 引言 在信息化、網絡化日益深入的今天&#xff0c;政府服務的方式也在不斷地變革與創新。政務熱線系統作為政府與民眾溝通的重要橋梁&#xff0c;其重要性不言而喻。政務熱線不僅是政府傾聽民眾聲音、回應社會關切的重要渠道&#xff0c;更是推動政府服務向數字化、智能化…

代碼隨想錄Day40:Leetcode343、96

Leetcode343&#xff1a; 問題描述&#xff1a; 給定一個正整數 n &#xff0c;將其拆分為 k 個 正整數 的和&#xff08; k > 2 &#xff09;&#xff0c;并使這些整數的乘積最大化。 返回 你可以獲得的最大乘積 。 代碼及注釋解析&#xff1a; class Solution { publ…

Linux-CentOS-7忘記密碼-修改登錄密碼圖文詳解

Linux-CentOS-7忘記密碼-修改登錄密碼圖文詳解 1.重啟系統&#xff1a; 在登錄界面&#xff0c;選擇要登錄的用戶并點擊"Power"按鈕&#xff0c;然后選擇"Restart"或"Reboot"重新啟動系統。 在系統啟動時持續按下 “e” 鍵進入編輯模式。 2…

谷歌 I/O 2024大會全面硬鋼OpenAI;騰訊宣布旗下的混元文生圖大模型;阿里巴巴技術下的AI自動視頻剪輯工具

? 1: 谷歌 I/O 2024 谷歌 I/O 2024 發布了眾多新技術&#xff0c;包括 Gemini AI、大語言模型和通用 AI 智能體等&#xff0c;全面顛覆搜索體驗。 谷歌 I/O 2024發布會帶來許多令人興奮的新功能和技術創新&#xff1a; Gemini 1.5 Pro&#xff1a;一個極其強大的語言模型&am…