修改设置->关于手机->法律信息 下有一些Item,如开放源代码许可、Google法律信息等,这里分析的是Android 4.4的代码

本文介绍了如何在Android设备的关于手机页面中添加自定义的'Google法律信息'选项,包括修改Settings源码、加载资源文件及确保对应Action的Activity在system/app目录下注册。
  • 修改设置->关于手机->法律信息 下有一些Item,如开放源代码许可、Google法律信息等,这里分析的是Android 4.4的代码

    如下图所示,想添加“Google法律信息”选项

    \

    查看Settings的源码在/Settings/src/com/android/settings/DeviceInfoSettings.java文件初始化时有这样一段代码

    01. @Override
    02. public void onCreate(Bundle icicle) {
    03. super.onCreate(icicle);
    04.  
    05. addPreferencesFromResource(R.xml.device_info_settings);
    06.  
    07. //............................................省略
    08.  
    09. /*
    10. * Settings is a generic app and should not contain any device-specific
    11. * info.
    12. */
    13. final Activity act = getActivity();
    14. // These are contained in the "container" preference group
    15. PreferenceGroup parentPreference = (PreferenceGroup) findPreference(KEY_CONTAINER);
    16. Utils.updatePreferenceToSpecificActivityOrRemove(act, parentPreference, KEY_TERMS,
    17. Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY);
    18. Utils.updatePreferenceToSpecificActivityOrRemove(act, parentPreference, KEY_LICENSE,
    19. Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY);
    20. Utils.updatePreferenceToSpecificActivityOrRemove(act, parentPreference, KEY_COPYRIGHT,
    21. Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY);
    22. Utils.updatePreferenceToSpecificActivityOrRemove(act, parentPreference, KEY_TEAM,
    23. Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY);
    24.  
    25. //..............................................省略
    26. }
    看加载的资源文件device_info_settings.xml

    001. <?xml version="1.0" encoding="utf-8"?>
    002. <!-- Copyright (C) 2008 The <a href="http://www.it165.net/pro/ydad/" target="_blank" class="keylink">Android</a> Open Source Project Licensed under the
    003. Apache License, Version 2.0 (the "License"); you may not use this file except
    004. in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
    005. Unless required by applicable law or agreed to in writing, software distributed
    006. under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
    007. OR CONDITIONS OF ANY KIND, either express or implied. See the License for
    008. the specific language governing permissions and limitations under the License. -->
    009.  
    010. <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
    011. android:title="@string/about_settings">
    012.  
    013. <!-- System update settings - launches activity -->
    014. <PreferenceScreen android:key="system_update_settings"
    015. android:title="@string/system_update_settings_list_item_title"
    016. android:summary="@string/system_update_settings_list_item_summary">
    017. <intent android:action="android.settings.SYSTEM_UPDATE_SETTINGS" />
    018. </PreferenceScreen>
    019. <PreferenceScreen android:key="software_update"
    020. android:title="@string/software_update">
    021. </PreferenceScreen>
    022. <PreferenceScreen android:key="mdm_fumo"
    023. android:title="@string/software_update">
    024. </PreferenceScreen>
    025. <!-- MOTA -->
    026. <PreferenceScreen android:key="mtk_system_update">
    027. <intent android:action="com.mediatek.intent.System_Update_Entry" />
    028. </PreferenceScreen>
    029.  
    030. <!-- software update service -->
    031. <PreferenceScreen android:key="scomo"
    032. android:title="@string/scomo_settings_title" android:summary="@string/software_updates_scomo_summary">
    033. <intent android:action="android.intent.action.MAIN"
    034. android:targetPackage="com.mediatek.dm" android:targetClass="com.mediatek.dm.scomo.DmScomoActivity" />
    035. </PreferenceScreen>
    036. <PreferenceScreen android:key="mdm_scomo"
    037. android:summary="@string/software_updates_scomo_summary"
    038. android:title="@string/scomo_settings_title">
    039. <intent android:action="android.intent.action.MAIN"
    040. android:targetClass="com.mediatek.mediatekdm.scomo.DmScomoActivity"
    041. android:targetPackage="com.mediatek.mediatekdm" />
    042. </PreferenceScreen>
    043.  
    044. <PreferenceScreen android:key="additional_system_update_settings"
    045. android:title="@string/additional_system_update_settings_list_item_title">
    046. <intent android:action="android.intent.action.MAIN"
    047. android:targetPackage="@string/additional_system_update"
    048. android:targetClass="@string/additional_system_update_menu" />
    049. </PreferenceScreen>
    050.  
    051. <!-- More Software updates - launches activity -->
    052. <PreferenceScreen android:key="more_software_updates"
    053. android:title="@string/software_updates_more_title" android:summary="@string/software_updates_more_summary">
    054. <intent android:targetPackage="com.android.settings"
    055. android:targetClass="com.mediatek.settings.deviceinfo.SoftwareUpdates" />
    056. </PreferenceScreen>
    057.  
    058. <!-- Device status - launches activity -->
    059. <PreferenceScreen android:key="status_info"
    060. android:title="@string/device_status" android:summary="@string/device_status_summary">
    061. <intent android:action="android.intent.action.MAIN"
    062. android:targetPackage="com.android.settings" android:targetClass="com.android.settings.deviceinfo.Status" />
    063. </PreferenceScreen>
    064.  
    065. <!-- Gemini Device status - launches Gemini activity -->
    066. <PreferenceScreen android:key="status_info_gemini"
    067. android:title="@string/device_status" android:summary="@string/device_status_summary">
    068. <intent android:action="android.intent.action.MAIN"
    069. android:targetPackage="com.android.settings" android:targetClass="com.mediatek.settings.deviceinfo.StatusGemini" />
    070. </PreferenceScreen>
    071.  
    072. <!-- Legal Information -->
    073. <PreferenceScreen android:key="container"
    074. android:title="@string/legal_information">
    075.  
    076. <!-- Note: The titles given here probably won't be used. Instead, we programmatically
    077. fill the title with the label of the activity with the corresponding action.
    078. If there is not an activity for an action, the item will be removed from
    079. the list. -->
    080.  
    081. <!-- Copyright information -->
    082. <PreferenceScreen android:key="copyright"
    083. android:title="@string/copyright_title">
    084. <intent android:action="android.settings.COPYRIGHT" />
    085. </PreferenceScreen>
    086.  
    087. <!-- License information -->
    088. <PreferenceScreen android:key="license"
    089. android:title="@string/license_title">
    090. <intent android:action="android.settings.LICENSE" />
    091. </PreferenceScreen>
    092.  
    093. <!-- Terms and conditions -->
    094. <PreferenceScreen android:key="terms" android:title="@string/terms_title">
    095. <intent android:action="android.settings.TERMS" />
    096. </PreferenceScreen>
    097.  
    098. </PreferenceScreen>
    099.  
    100. <PreferenceScreen android:key="safetylegal"
    101. android:title="@string/settings_safetylegal_title">
    102. <intent android:action="android.settings.SAFETY" />
    103. </PreferenceScreen>
    104.  
    105. <!-- Contributors -->
    106. <!-- <PreferenceScreen android:key="contributors" android:title="@string/contributors_title">
    107. <intent android:action="android.settings.TEAM" /> </PreferenceScreen> -->
    108.  
    109. <PreferenceScreen
    110. android:key="regulatory_info"
    111. android:title="@string/regulatory_information">
    112. <intent android:action="android.settings.SHOW_REGULATORY_INFO" />
    113. </PreferenceScreen>
    114.  
    115. <!-- Device hardware model -->
    116. <Preference android:key="device_model"
    117. style="?android:preferenceInformationStyle" android:title="@string/model_number"
    118. android:summary="@string/device_info_default" />
    119.  
    120. <!-- Device firmware version -->
    121. <Preference android:key="firmware_version"
    122. style="?android:preferenceInformationStyle" android:title="@string/firmware_version"
    123. android:summary="@string/device_info_default" />
    124.  
    125. <!-- Device FCC equipment id -->
    126. <Preference android:key="fcc_equipment_id"
    127. style="?android:preferenceInformationStyle" android:title="@string/fcc_equipment_id"
    128. android:summary="@string/device_info_default" />
    129.  
    130. <!-- Device Baseband version -->
    131. <Preference android:key="baseband_version"
    132. style="?android:preferenceInformationStyle" android:title="@string/baseband_version"
    133. android:summary="@string/device_info_default" />
    134.  
    135. <!-- Device 2nd Baseband version -->
    136. <Preference android:key="baseband_version_2"
    137. style="?android:preferenceInformationStyle" android:title="@string/baseband_version"
    138. android:summary="@string/device_info_default" />
    139.  
    140. <!-- Device Kernel version -->
    141. <Preference android:key="kernel_version"
    142. style="?android:preferenceInformationStyle" android:title="@string/kernel_version"
    143. android:summary="@string/device_info_default" />
    144.  
    145. <!-- Detailed build version -->
    146. <Preference android:key="build_number"
    147. style="?android:preferenceInformationStyle" android:title="@string/build_number"
    148. android:summary="@string/device_info_default" />
    149.  
    150. <!-- Detailed customer build version -->
    151. <Preference android:key="custom_build_version"
    152. style="?android:preferenceInformationStyle" android:title="@string/custom_build_version"
    153. android:summary="@string/device_info_default" />
    154.  
    155. <!-- SE<a href="http://www.it165.net/os/oslin/" target="_blank" class="keylink">Linux</a> status information -->
    156. <Preference android:key="selinux_status"
    157. style="?android:preferenceInformationStyle" android:title="@string/selinux_status"
    158. android:summary="@string/selinux_status_enforcing" />
    159.  
    160. </PreferenceScreen>
    在这个xml中配置的Action就是整个《关于手机》上的所有Item,从上面的信息中可以找到《法律信息》选项,有三个子项目

    01. <!-- Legal Information -->
    02. <PreferenceScreen android:key="container"
    03. android:title="@string/legal_information">
    04.  
    05. <!-- Note: The titles given here probably won't be used. Instead, we programmatically
    06. fill the title with the label of the activity with the corresponding action.
    07. If there is not an activity for an action, the item will be removed from
    08. the list. -->
    09.  
    10. <!-- Copyright information -->
    11. <PreferenceScreen android:key="copyright"
    12. android:title="@string/copyright_title">
    13. <intent android:action="android.settings.COPYRIGHT" />
    14. </PreferenceScreen>
    15.  
    16. <!-- License information -->
    17. <PreferenceScreen android:key="license"
    18. android:title="@string/license_title">
    19. <intent android:action="android.settings.LICENSE" />
    20. </PreferenceScreen>
    21.  
    22. <!-- Terms and conditions -->
    23. <PreferenceScreen android:key="terms" android:title="@string/terms_title">
    24. <intent android:action="android.settings.TERMS" />
    25. </PreferenceScreen>
    26.  
    27. </PreferenceScreen>
    以上选项是否会显示出来是由updatePreferenceToSpecificActivityOrRemove函数处理,这个函数在/Settings/src/com/android/settings/Utils.java

    01. public static boolean updatePreferenceToSpecificActivityOrRemove(Context context,
    02. PreferenceGroup parentPreferenceGroup, String preferenceKey, int flags) {
    03.  
    04. Preference preference = parentPreferenceGroup.findPreference(preferenceKey);
    05. if (preference == null) {
    06. return false;
    07. }
    08.  
    09. Intent intent = preference.getIntent();
    10. if (intent != null) {
    11. // Find the activity that is in the system image
    12. PackageManager pm = context.getPackageManager();
    13. List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
    14. int listSize = list.size();
    15. for (int i = 0; i < listSize; i++) {
    16. ResolveInfo resolveInfo = list.get(i);
    17. if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
    18. != 0) {
    19.  
    20. // Replace the intent with this specific activity
    21. preference.setIntent(new Intent().setClassName(
    22. resolveInfo.activityInfo.packageName,
    23. resolveInfo.activityInfo.name));
    24.  
    25. if ((flags & UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY) != 0) {
    26. // Set the preference title to the activity's label
    27. preference.setTitle(resolveInfo.loadLabel(pm));
    28. }
    29.  
    30. return true;
    31. }
    32. }
    33. }
    34.  
    35. // Did not find a matching activity, so remove the preference
    36. parentPreferenceGroup.removePreference(preference);
    37.  
    38. return false;
    39. }
    如在Settings中会调用

    Utils.updatePreferenceToSpecificActivityOrRemove(act, parentPreference, KEY_TERMS, Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY);

    去查找是否存在安装在/system/app/目录下对应的Action,如果存在就不处理,否则就不显示该Item。

    目前我的手机上没有显示《Google法律信息》选项,说明没有找到注册“android.settings.TERMS”这个Action的Activity,就不显示该Item,我们可以写个测试程序,去注册“android.settings.TERMS”

    01. <?xml version="1.0" encoding="utf-8"?>
    02. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    03. package="com.example.cts_7301"
    04. android:versionCode="1"
    05. android:versionName="1.0" >
    06.  
    07. <uses-sdk
    08. android:minSdkVersion="11"
    09. android:targetSdkVersion="19" />
    10.  
    11. <application
    12. android:allowBackup="true"
    13. android:icon="@drawable/ic_launcher"
    14. android:label="@string/app_name"
    15. android:theme="@style/AppTheme" >
    16.  
    17. <activity
    18. android:name="com.example.cts_7301.activi"
    19. android:label="@string/app_name" >
    20. <intent-filter>
    21. <action android:name="android.intent.action.MAIN" />
    22.  
    23. <category android:name="android.intent.category.LAUNCHER" />
    24. </intent-filter>
    25. </activity>
    26.  
    27. <activity android:name="com.example.cts_7301.termsActivity" >
    28. <intent-filter>
    29. <action android:name="android.settings.TERMS" />
    30.  
    31. <category android:name="android.intent.category.DEFAULT" />
    32. </intent-filter>
    33. </activity>
    34. </application>
    35.  
    36. </manifest>
    然后把这个Apk安装到/system/app/目录下

    1. dzt@dzt-All-Series:~/workspace/cts_7301/bin$ ls
    2. AndroidManifest.xml  classes  classes.dex  cts_7301.apk  dexedLibs  res  resources.ap_
    3. dzt@dzt-All-Series:~/workspace/cts_7301/bin$ adb push cts_7301.apk /system/app/
    4. 3474 KB/s (549968 bytes in 0.154s)
    5. dzt@dzt-All-Series:~/workspace/cts_7301/bin$ adb push cts_7301.apk /system/app/
    6. 3242 KB/s (549968 bytes in 0.165s)
    7. dzt@dzt-All-Series:~/workspace/cts_7301/bin$
    把Settings App完全关闭后再启动,此时就能看到刚刚注册的Action就能添加到Item

    \
    logcat 

    01. 01-02 12:35:01.893 I/dzt     (19718): ApplicationPackageManager---queryIntentActivitiesAsUser intent = Intent { act=android.settings.TERMS } flags = 0 userId = 0
    02. 01-02 12:35:01.895 I/dzt     (19718): ApplicationPackageManager---queryIntentActivitiesAsUser list = [ResolveInfo{42a88520 com.example.cts_7301/.termsActivity m=0x108000}]
    03. 01-02 12:35:01.897 I/dzt     (19718): ApplicationPackageManager---queryIntentActivitiesAsUser intent = Intent { act=android.settings.LICENSE } flags = 0 userId = 0
    04. 01-02 12:35:01.899 I/dzt     (19718): ApplicationPackageManager---queryIntentActivitiesAsUser list = [ResolveInfo{42a91e68 com.android.settings/.SettingsLicenseActivity m=0x108000}]
    05. 01-02 12:35:01.901 I/dzt     (19718): ApplicationPackageManager---queryIntentActivitiesAsUser intent = Intent { act=android.settings.COPYRIGHT } flags = 0 userId = 0
    06. 01-02 12:35:01.902 I/dzt     (19718): ApplicationPackageManager---queryIntentActivitiesAsUser list = []
    07. 01-02 12:35:01.902 I/dzt     (19718): ApplicationPackageManager---queryIntentActivitiesAsUser intent = Intent { act=android.settings.SYSTEM_UPDATE_SETTINGS } flags = 0 userId = 0
    08. 01-02 12:35:01.904 I/dzt     (19718): ApplicationPackageManager---queryIntentActivitiesAsUser list = []
    09. 01-02 12:35:01.937 I/dzt     (19718): ApplicationPackageManager---queryIntentActivitiesAsUser intent = Intent { act=com.android.settings.OPERATOR_APPLICATION_SETTING } flags = 128 userId = 0
    10. 01-02 12:35:01.938 I/dzt     (19718): ApplicationPackageManager---queryIntentActivitiesAsUser list = []
    11. 01-02 12:35:01.941 I/dzt     (19718): ApplicationPackageManager---queryIntentActivitiesAsUser intent = Intent { act=com.android.settings.MANUFACTURER_APPLICATION_SETTING } flags = 128 userId = 0
    12. 01-02 12:35:01.943 I/dzt     (19718): ApplicationPackageManager---queryIntentActivitiesAsUser list = []
    13. 01-02 12:35:02.177 I/dzt     (19718): ApplicationPackageManager---queryIntentActivitiesAsUser intent = Intent { act=com.android.settings.OPERATOR_APPLICATION_SETTING } flags = 128 userId = 0
    14. 01-02 12:35:02.179 I/dzt     (19718): ApplicationPackageManager---queryIntentActivitiesAsUser list = []
    15. 01-02 12:35:02.180 I/dzt     (19718): ApplicationPackageManager---queryIntentActivitiesAsUser intent = Intent { act=com.android.settings.MANUFACTURER_APPLICATION_SETTING } flags = 128 userId = 0
    16. 01-02 12:35:02.181 I/dzt     (19718): ApplicationPackageManager---queryIntentActivitiesAsUser list = []
    由于PackageManager是一个抽象类,调用queryIntentActivitiesAsUser最终会调用/frameworks/base/core/java/android/app/ApplicationPackageManager.java类的

    01. @Override
    02. public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent,
    03. int flags, int userId) {
    04. Log.i("dzt""ApplicationPackageManager---queryIntentActivitiesAsUser intent = " + intent.toString() + " flags = "+flags + " userId = "+userId);
    05. try {
    06. List<ResolveInfo> list = mPM.queryIntentActivities(
    07. intent,
    08. intent.resolveTypeIfNeeded(mContext.getContentResolver()),
    09. flags,
    10. userId);
    11. Log.i("dzt""ApplicationPackageManager---queryIntentActivitiesAsUser list = " + list.toString());
    12. return list;
    13. catch (RemoteException e) {
    14. throw new RuntimeException("Package manager has died", e);
    15. }
    16. }
    这说明《法律信息》中的三个Item需要有Activity注册了对应的Action才会显示出来,不一定是Settings App也可以是其它的apk,但必需是安装在/system/app/目录下的才行。
WARNING: Enabling and disabling experimental features do not take effect until next start of PowerShell. WARNING: Enabling and disabling experimental features do not take effect until next start of PowerShell. [PS7 19:25:05] C:\Users\Administrator\Desktop [master] > # 增强版 Install-Socat 函数(修复下载源问题) [PS7 19:25:13] C:\Users\Administrator\Desktop [master] > function Install-Socat { >> param( >> [Parameter(Mandatory = $false)] >> [string]$InstallDrive = "E:", >> >> [Parameter(Mandatory = $false)] >> [string]$InstallDir = "Program Files\Socat" >> ) >> >> # 1. 规范化路径处理 >> $fullPath = [System.IO.Path]::GetFullPath("$InstallDrive\$InstallDir").TrimEnd('\') >> >> # 2. 验证文件系统权限 >> if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { >> Write-Warning "需要管理员权限安装程序" >> return $null >> } >> >> # 3. 检查现有安装 >> $existingPath = (Get-Command socat -ErrorAction SilentlyContinue | >> Select-Object -ExpandProperty Source -First 1) ?? $null >> >> if ($existingPath) { >> Write-Host "已安装于: $existingPath" -ForegroundColor Green >> return $existingPath >> } >> >> # 4. 创建安装目录 >> try { >> if (-not (Test-Path $fullPath)) { >> $null = New-Item -Path $fullPath -ItemType Directory -Force >> } >> } catch { >> Write-Error "创建目录失败: $($_.Exception.Message)" >> return $null >> } >> >> # 5. 文件下载(更新有效的下载源) >> $tempFile = Join-Path $env:TEMP "socat-win64.zip" >> $downloadUrls = @( >> # 官方镜像源 >> "https://github.com/portapps/socat-portable/releases/download/1.7.4.4/socat-1.7.4.4-win64.zip", >> "https://downloads.sourceforge.net/project/unix-utils/socat/1.7.4.4/socat-1.7.4.4-win64.zip?ts=$(Get-Date -UFormat %s)", >> >> # 备用镜像源 >> "https://github.com/portapps/socat-portable/releases/download/1.7.4.4/socat-1.7.4.4-win64.zip", >> "https://cfhcable.dl.sourceforge.net/project/unix-utils/socat/1.7.4.4/socat-1.7.4.4-win64.zip", >> "https://altushost-swe.dl.sourceforge.net/project/unix-utils/socat/1.7.4.4/socat-1.7.4.4-win64.zip" >> ) >> >> $validDownload = $false >> $lastError = $null >> >> foreach ($downloadUrl in $downloadUrls) { >> try { >> # 清除旧下载 >> if (Test-Path $tempFile) { >> Remove-Item $tempFile -Force -ErrorAction SilentlyContinue >> } >> >> # 下载文件 >> Write-Host "尝试从 $downloadUrl 下载..." -ForegroundColor Cyan >> $ProgressPreference = 'SilentlyContinue' >> >> try { >> Invoke-WebRequest $downloadUrl -OutFile $tempFile -UseBasicParsing -RetryIntervalSec 3 -ErrorAction Stop >> } catch { >> $lastError = $_.Exception.Message >> Write-Warning "下载失败 ($downloadUrl): $lastError" >> continue >> } >> >> # 验证文件大小(最小1MB) >> $fileInfo = Get-Item $tempFile -ErrorAction Stop >> if ($fileInfo.Length -lt 1MB) { >> $lastError = "文件过小 ($([math]::Round($fileInfo.Length/1KB)) KB)" >> Write-Warning "文件校验失败: $lastError" >> continue >> } >> >> # 验证ZIP文件完整性 >> try { >> Add-Type -AssemblyName System.IO.Compression.FileSystem -ErrorAction Stop >> $archive = [System.IO.Compression.ZipFile]::OpenRead($tempFile) >> $entryCount = $archive.Entries.Count >> $archive.Dispose() >> >> if ($entryCount -eq 0) { >> $lastError = "空压缩包" >> Write-Warning "文件校验失败: $lastError" >> continue >> } >> >> $validDownload = $true >> Write-Host "成功下载并验证: $downloadUrl" -ForegroundColor Green >> break >> } catch { >> $lastError = $_.Exception.Message >> Write-Warning "文件校验失败: $lastError" >> } >> } catch { >> $lastError = $_.Exception.Message >> Write-Warning "下载过程中出错: $lastError" >> } finally { >> $ProgressPreference = 'Continue' >> } >> } >> >> if (-not $validDownload) { >> Write-Error "所有下载源均失败。最后错误: $lastError" >> Write-Host @" >> 无法自动下载socat。请手动执行以下操作: >> 1. 从以下链接下载socat: >> https://github.com/portapps/socat-portable/releases >> 2. 解压到: $fullPath >> 3. 将以下路径添加到系统PATH环境变量: >> $fullPath >> "@ -ForegroundColor Yellow >> return $null >> } >> >> # 6. 解压缩 >> try { >> # 确保目标目录存在 >> if (-not (Test-Path $fullPath)) { >> $null = New-Item -Path $fullPath -ItemType Directory -Force >> } >> >> # 使用.NET方法解压 >> Add-Type -AssemblyName System.IO.Compression.FileSystem >> [System.IO.Compression.ZipFile]::ExtractToDirectory($tempFile, $fullPath) >> >> $socatExe = Join-Path $fullPath "socat.exe" >> >> if (-not (Test-Path $socatExe -PathType Leaf)) { >> throw "解压后未找到 socat.exe" >> } >> } catch { >> Write-Error "解压失败: $($_.Exception.Message)" >> return $null >> } >> >> # 7. 环境变量更新 >> $currentPath = [Environment]::GetEnvironmentVariable("Path", "Machine") >> if ($currentPath -notlike "*$fullPath*") { >> $newPath = $currentPath + ";$fullPath" >> [Environment]::SetEnvironmentVariable("Path", $newPath, "Machine") >> } >> >> # 8. 更新当前会话PATH >> $env:Path += ";$fullPath" >> >> # 9. 返回完整路径 >> if (Test-Path $socatExe) { >> Write-Host "socat 安装成功: $socatExe" -ForegroundColor Green >> return $socatExe.ToString() >> } else { >> Write-Error "安装后文件验证失败" >> return $null >> } >> } [PS7 19:25:14] C:\Users\Administrator\Desktop [master] > [PS7 19:25:14] C:\Users\Administrator\Desktop [master] > # 增强版 New-TcpTunnel 函数(保持不变) [PS7 19:25:14] C:\Users\Administrator\Desktop [master] > function New-TcpTunnel { >> param( >> [int]$LocalPort = 8443, >> [string]$RemoteHost = "google.com", >> [int]$RemotePort = 443, >> [string]$SocatPath = $null >> ) >> >> # 1. 获取socat路径 >> $socatBinary = if ($SocatPath -and (Test-Path $SocatPath)) { >> $SocatPath.ToString() >> } else { >> $installedPath = Install-Socat >> if (-not $installedPath) { >> throw "socat安装失败" >> } >> $installedPath.ToString() >> } >> >> # 2. 创建日志文件 >> $logTime = Get-Date -Format "yyyyMMdd-HHmmss" >> $stdOutFile = Join-Path $env:TEMP "socat-$LocalPort-$logTime-out.log" >> $stdErrFile = Join-Path $env:TEMP "socat-$LocalPort-$logTime-err.log" >> >> # 3. 启动进程 >> try { >> $processArgs = @{ >> FilePath = $socatBinary >> ArgumentList = "TCP-LISTEN:$LocalPort,fork,reuseaddr TCP:$($RemoteHost):$RemotePort" >> NoNewWindow = $true >> RedirectStandardOutput = $stdOutFile >> RedirectStandardError = $stdErrFile >> PassThru = $true >> } >> >> $process = Start-Process @processArgs >> } catch { >> Write-Error "启动socat失败: $($_.Exception.Message)" >> if (Test-Path $stdErrFile) { >> Write-Warning "错误日志内容: $(Get-Content $stdErrFile -Raw)" >> } >> throw >> } >> >> # 4. 验证端口监听 >> $portActive = $false >> 1..5 | ForEach-Object { >> Start-Sleep -Seconds 1 >> if (-not $portActive) { >> $portStatus = Get-NetTCPConnection -LocalPort $LocalPort -ErrorAction SilentlyContinue >> $portActive = [bool]$portStatus >> } >> } >> >> if (-not $portActive) { >> Write-Warning "端口 $LocalPort 状态: 未监听" >> if (Test-Path $stdErrFile) { >> Write-Warning "错误日志: $(Get-Content $stdErrFile -Raw)" >> } >> throw "隧道创建失败,端口 $LocalPort 未监听" >> } >> >> # 5. 返回进程信息 >> return [PSCustomObject]@{ >> Process = $process >> Port = $LocalPort >> StdOutLog = $stdOutFile >> StdErrLog = $stdErrFile >> } >> } [PS7 19:25:14] C:\Users\Administrator\Desktop [master] > # 测试安装函数 [PS7 19:25:20] C:\Users\Administrator\Desktop [master] > $socatPath = Install-Socat -Verbose 尝试从 https://github.com/portapps/socat-portable/releases/download/1.7.4.4/socat-1.7.4.4-win64.zip 下载... VERBOSE: Requested HTTP/1.1 GET with 0-byte payload VERBOSE: Received HTTP/1.1 9-byte response of content type text/plain WARNING: 下载失败 (https://github.com/portapps/socat-portable/releases/download/1.7.4.4/socat-1.7.4.4-win64.zip): Response status code does not indicate success: 404 (Not Found). 尝试从 https://downloads.sourceforge.net/project/unix-utils/socat/1.7.4.4/socat-1.7.4.4-win64.zip?ts=1757157921 下载... VERBOSE: Requested HTTP/1.1 GET with 0-byte payload VERBOSE: Received HTTP/1.1 response of content type text/html of unknown size VERBOSE: File Name: socat-win64.zip WARNING: 文件校验失败: 文件过小 (102 KB) 尝试从 https://github.com/portapps/socat-portable/releases/download/1.7.4.4/socat-1.7.4.4-win64.zip 下载... VERBOSE: Requested HTTP/1.1 GET with 0-byte payload VERBOSE: Received HTTP/1.1 9-byte response of content type text/plain WARNING: 下载失败 (https://github.com/portapps/socat-portable/releases/download/1.7.4.4/socat-1.7.4.4-win64.zip): Response status code does not indicate success: 404 (Not Found). 尝试从 https://cfhcable.dl.sourceforge.net/project/unix-utils/socat/1.7.4.4/socat-1.7.4.4-win64.zip 下载... VERBOSE: Requested HTTP/1.1 GET with 0-byte payload WARNING: 下载失败 (https://cfhcable.dl.sourceforge.net/project/unix-utils/socat/1.7.4.4/socat-1.7.4.4-win64.zip): 由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败。 (cfhcable.dl.sourceforge.net:443) 尝试从 https://altushost-swe.dl.sourceforge.net/project/unix-utils/socat/1.7.4.4/socat-1.7.4.4-win64.zip 下载... VERBOSE: Requested HTTP/1.1 GET with 0-byte payload VERBOSE: Received HTTP/1.1 response of content type text/html of unknown size VERBOSE: File Name: socat-win64.zip WARNING: 文件校验失败: 文件过小 (102 KB) Install-Socat: 所有下载源均失败。最后错误: 文件过小 (102 KB) 无法自动下载socat。请手动执行以下操作: 1. 从以下链接下载socat: https://github.com/portapps/socat-portable/releases 2. 解压到: E:\Program Files\Socat 3. 将以下路径添加到系统PATH环境变量: E:\Program Files\Socat [PS7 19:25:58] C:\Users\Administrator\Desktop [master] > [PS7 19:25:58] C:\Users\Administrator\Desktop [master] > # 验证安装 [PS7 19:25:58] C:\Users\Administrator\Desktop [master] > if ($socatPath) { >> socat -V >> Write-Host "socat 安装成功" -ForegroundColor Green >> } [PS7 19:25:58] C:\Users\Administrator\Desktop [master] > # 创建到HTTP服务的隧道 [PS7 19:26:10] C:\Users\Administrator\Desktop [master] > $tunnel = New-TcpTunnel -LocalPort 8080 -RemoteHost "example.com" -RemotePort 80 尝试从 https://github.com/portapps/socat-portable/releases/download/1.7.4.4/socat-1.7.4.4-win64.zip 下载... WARNING: 下载失败 (https://github.com/portapps/socat-portable/releases/download/1.7.4.4/socat-1.7.4.4-win64.zip): Response status code does not indicate success: 404 (Not Found). 尝试从 https://downloads.sourceforge.net/project/unix-utils/socat/1.7.4.4/socat-1.7.4.4-win64.zip?ts=1757157971 下载... WARNING: 文件校验失败: 文件过小 (102 KB) 尝试从 https://github.com/portapps/socat-portable/releases/download/1.7.4.4/socat-1.7.4.4-win64.zip 下载... WARNING: 下载失败 (https://github.com/portapps/socat-portable/releases/download/1.7.4.4/socat-1.7.4.4-win64.zip): Response status code does not indicate success: 404 (Not Found). 尝试从 https://cfhcable.dl.sourceforge.net/project/unix-utils/socat/1.7.4.4/socat-1.7.4.4-win64.zip 下载... WARNING: 下载失败 (https://cfhcable.dl.sourceforge.net/project/unix-utils/socat/1.7.4.4/socat-1.7.4.4-win64.zip): 由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败。 (cfhcable.dl.sourceforge.net:443) 尝试从 https://altushost-swe.dl.sourceforge.net/project/unix-utils/socat/1.7.4.4/socat-1.7.4.4-win64.zip 下载... WARNING: 文件校验失败: 文件过小 (102 KB) Install-Socat: Line | 13 | $installedPath = Install-Socat | ~~~~~~~~~~~~~ | 所有下载源均失败。最后错误: 文件过小 (102 KB) 无法自动下载socat。请手动执行以下操作: 1. 从以下链接下载socat: https://github.com/portapps/socat-portable/releases 2. 解压到: E:\Program Files\Socat 3. 将以下路径添加到系统PATH环境变量: E:\Program Files\Socat Exception: Line | 15 | throw "socat安装失败" | ~~~~~~~~~~~~~~~~~ | socat安装失败 [PS7 19:26:48] C:\Users\Administrator\Desktop [master] > [PS7 19:26:48] C:\Users\Administrator\Desktop [master] > # 测试隧道 [PS7 19:26:48] C:\Users\Administrator\Desktop [master] > Start-Process "http://localhost:$($tunnel.Port)" [PS7 19:26:48] C:\Users\Administrator\Desktop [master] > [PS7 19:26:48] C:\Users\Administrator\Desktop [master] > # 监控日志 [PS7 19:26:48] C:\Users\Administrator\Desktop [master] > Get-Content $tunnel.StdErrLog -Wait -Tail 10 Get-Content: Cannot bind argument to parameter 'Path' because it is null. [PS7 19:26:48] C:\Users\Administrator\Desktop [master] > $cacheFile = "C:\ProgramData\socat-cache\socat-win64.zip" [PS7 19:27:07] C:\Users\Administrator\Desktop [master] > if (Test-Path $cacheFile) { >> Copy-Item $cacheFile $tempFile >> $validDownload = $true >> } [PS7 19:27:07] C:\Users\Administrator\Desktop [master] >
最新发布
09-07
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值