ShellCommand.java

本文介绍了一个用于在Android环境中运行shell命令的Java类ShellCommand。该类可以执行命令并返回标准输入输出,支持通过su权限执行命令。文章展示了如何使用此类获取WiFi接口名称的例子。

/**
 * ShellCommand.java runs commands as if in a native shell instance, and can return stdio.
 *
 * Code by Kevin(at)TeslaCoil
 * Adapted by LouZiffer(at)SDX
 *
 * Example usage (use cmd.su.runWaitFor instead of cmd.sh.runWaitFor to run as su):
 *
 * ShellCommand cmd = new ShellCommand();
 * CommandResult r = cmd.sh.runWaitFor("/system/bin/getprop wifi.interface");
 *
 * if (!r.success()) {
 *     Log.d(MSG_TAG, "Error " + r.stderr);
 * } else {
 *     Log.d(MSG_TAG, "Successfully executed getprop wifi.interface. Result: " + r.stdout);
 *     this.tetherNetworkDevice = (r.stdout);
 * }
 */

package m900.tether.system;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;

import android.util.Log;

public class ShellCommand {
    private static final String TAG = "ShellCommand.java";
    private Boolean can_su;   

    public SH sh;
    public SH su;
   
    public ShellCommand() {
        sh = new SH("sh");
        su = new SH("su");
    }
   
    public boolean canSU() {
        return canSU(false);
    }
   
    public boolean canSU(boolean force_check) {
        if (can_su == null || force_check) {
            CommandResult r = su.runWaitFor("id");
            StringBuilder out = new StringBuilder();
           
            if (r.stdout != null)
                out.append(r.stdout).append(" ; ");
            if (r.stderr != null)
                out.append(r.stderr);
           
            Log.v(TAG, "canSU() su[" + r.exit_value + "]: " + out);
            can_su = r.success();
        }
        return can_su;
    }

    public SH suOrSH() {
        return canSU() ? su : sh;
    }
   
    public class CommandResult {
        public final String stdout;
        public final String stderr;
        public final Integer exit_value;
       
        CommandResult(Integer exit_value_in, String stdout_in, String stderr_in)
        {
            exit_value = exit_value_in;
            stdout = stdout_in;
            stderr = stderr_in;
        }
       
        CommandResult(Integer exit_value_in) {
            this(exit_value_in, null, null);
        }
       
        public boolean success() {
            return exit_value != null && exit_value == 0;
        }
    }

    public class SH {
        private String SHELL = "sh";

        public SH(String SHELL_in) {
            SHELL = SHELL_in;
        }

        public Process run(String s) {
            Process process = null;
            try {
                process = Runtime.getRuntime().exec(SHELL);
                DataOutputStream toProcess = new DataOutputStream(process.getOutputStream());
                toProcess.writeBytes("exec " + s + "\n");
                toProcess.flush();
            } catch(Exception e) {
                Log.e(TAG, "Exception while trying to run: '" + s + "' " + e.getMessage());
                process = null;
            }
            return process;
        }
       
        private String getStreamLines(InputStream is) {
            String out = null;
            StringBuffer buffer = null;
            DataInputStream dis = new DataInputStream(is);

            try {
                if (dis.available() > 0) {
                    buffer = new StringBuffer(dis.readLine());
                    while(dis.available() > 0)
                        buffer.append("\n").append(dis.readLine());
                }
                dis.close();
            } catch (Exception ex) {
                Log.e(TAG, ex.getMessage());
            }
            if (buffer != null)
                out = buffer.toString();
            return out;
        }

        public CommandResult runWaitFor(String s) {
            Process process = run(s);
            Integer exit_value = null;
            String stdout = null;
            String stderr = null;
            if (process != null) {
                try {
                    exit_value = process.waitFor();
                   
                    stdout = getStreamLines(process.getInputStream());
                    stderr = getStreamLines(process.getErrorStream());
                   
                } catch(InterruptedException e) {
                    Log.e(TAG, "runWaitFor " + e.toString());
                } catch(NullPointerException e) {
                    Log.e(TAG, "runWaitFor " + e.toString());
                }
            }
            return new CommandResult(exit_value, stdout, stderr);
        }
    }
}

C:\Users\1\Desktop>adb shell pm disable-user com.bbk.updater # 验证状态 adb shell pm list packages -d | grep 'updater' Exception occurred while executing 'disable-user': java.lang.SecurityException: Cannot disable com.bbk.updater no root permission at com.android.server.pm.PackageManagerService.setEnabledSettingInternalLocked(PackageManagerService.java:4579) at com.android.server.pm.PackageManagerService.setEnabledSettings(PackageManagerService.java:4483) at com.android.server.pm.PackageManagerService.-$$Nest$msetEnabledSettings(PackageManagerService.java:0) at com.android.server.pm.PackageManagerService$IPackageManagerImpl.setApplicationEnabledSetting(PackageManagerService.java:6381) at com.android.server.pm.PackageManagerShellCommand.runSetEnabledSetting(PackageManagerShellCommand.java:2572) at com.android.server.pm.PackageManagerShellCommand.onCommand(PackageManagerShellCommand.java:296) at com.android.modules.utils.BasicShellCommandHandler.exec(BasicShellCommandHandler.java:97) at android.os.ShellCommand.exec(ShellCommand.java:38) at com.android.server.pm.PackageManagerService$IPackageManagerImpl.onShellCommand(PackageManagerService.java:7029) at android.os.Binder.shellCommand(Binder.java:1134) at android.os.Binder.onTransact(Binder.java:954) at android.content.pm.IPackageManager$Stub.onTransact(IPackageManager.java:5024) at com.android.server.pm.PackageManagerService$IPackageManagerImpl.onTransact(PackageManagerService.java:7013) at android.os.Binder.execTransactInternal(Binder.java:1410) at android.os.Binder.execTransact(Binder.java:1341)
最新发布
06-29
Failed to create session. An unknown server-side error occurred while processing the command. Original error: Error executing adbExec. Original error: 'Command 'D:\\tools\\platform-tools\\adb.exe -P 5037 -s e9ce79af shell 'settings delete global hidden_api_policy_pre_p_apps;settings delete global hidden_api_policy_p_apps;settings delete global hidden_api_policy'' exited with code 255'; Command output: Exception occurred while executing 'delete': java.lang.SecurityException: Permission denial, must have one of: [android.permission.WRITE_SECURE_SETTINGS] at com.android.providers.settings.SettingsProvider.enforceHasAtLeastOnePermission(SettingsProvider.java:2705) at com.android.providers.settings.SettingsProvider.mutateGlobalSetting(SettingsProvider.java:1610) at com.android.providers.settings.SettingsProvider.mutateGlobalSetting(SettingsProvider.java:1602) at com.android.providers.settings.SettingsProvider.deleteGlobalSetting(SettingsProvider.java:1573) at com.android.providers.settings.SettingsProvider.call(SettingsProvider.java:597) at android.content.ContentProvider.call(ContentProvider.java:2716) at android.content.ContentProvider$Transport.call(ContentProvider.java:641) at com.android.providers.settings.SettingsService$MyShellCommand.deleteForUser(SettingsService.java:411) at com.android.providers.settings.SettingsService$MyShellCommand.onCommand(SettingsService.java:285) at com.android.modules.utils.BasicShellCommandHandler.exec(BasicShellCommandHandler.java:97) at android.os.ShellCommand.exec(ShellCommand.java:38) at com.android.providers.settings.SettingsService.onShellCommand(SettingsService.java:53) at
03-20
<think>好的,用户想解决Android安装时遇到的SecurityException异常,特别是提到PackageManagerShellCommand的doCreateSession方法中的问题。首先,我需要回忆一下相关的引用内容,看看有没有有用的信息。 引用[3]提到,新版本的Android限制了非shell和root用户安装非系统用户的app,比如system用户不能安装uid为7000的应用。这可能涉及到权限问题。引用[4]中的错误信息显示“install operation deny”,说明安装操作被拒绝,可能权限不足或者用户身份不对。 接下来,用户提到的错误可能发生在createSession的时候,比如引用[2]中的NullPointerException,但这里用户的问题是关于SecurityException,所以需要聚焦在权限和安全限制上。 首先,常见的原因可能有安装权限未正确声明,比如AndroidManifest.xml中没有请求INSTALL_PACKAGES权限。不过这个权限是系统级别的,普通应用无法获取。所以可能用户的应用尝试静默安装,而没有正确的系统签名或权限。 另外,用户身份的问题,比如应用运行在非shell或非root用户下,无法调用安装API。比如如果应用以system用户运行,但尝试安装非系统应用,就会被阻止,如引用[3]所述。 解决方案方面,需要检查应用是否有正确的权限,比如是否是系统应用,或者是否有系统签名。如果是adb安装,可能需要检查是否启用了安装来源权限,或者USB调试是否开启。 另外,用户可能在使用某些API时没有正确处理会话,比如使用PackageInstaller创建会话时缺少必要的参数或权限。需要确保在创建安装会话时传递正确的参数,比如设置安装来源或用户权限。 可能还需要检查AppOpsManager的调用是否正确,避免空指针,如引用[2]提到的,但这里主要解决SecurityException,所以重点在权限和用户身份。 最后,可能需要使用adb命令的特殊权限,或者将应用标记为允许覆盖安装,比如使用-allow-downgrade参数。如果是系统应用,可能需要系统签名或特权权限。 总结步骤:检查权限声明、确认用户身份、处理安装来源、使用正确的安装API参数、系统应用配置等。需要一步步引导用户排查这些可能性,并提供具体的代码示例和配置修改建议。</think>### Android安装过程中SecurityException异常分析与解决方案 #### 一、异常触发原因分析 1. **用户权限限制** Android系统从8.0开始,对非shell/root用户安装非系统应用增加了限制。例如: - 普通用户进程(如system用户)尝试安装其他用户归属的APK时会被拒绝[^3] - 未声明`android.permission.INSTALL_PACKAGES`权限的应用尝试静默安装 2. **会话创建限制** 调用`PackageInstaller.createSession()`时可能因以下原因失败: - 缺少`android.Manifest.permission.INSTALL_PACKAGES`权限 - 未正确设置`PackageInstaller.SessionParams`参数(如未设置安装来源) 3. **安全策略冲突** ```java // 典型错误堆栈 SecurityException: install operation deny at com.android.server.pm.PackageManagerShellCommand.doCreateSession(PackageManagerShellCommand.java:1500) ``` 该错误表明安装请求不符合系统安全策略,常见于: - 非系统应用尝试使用`adb shell pm install`安装APK - 目标API级别与设备安全策略冲突 --- #### 二、解决方案分步说明 1. **基础权限配置** 在`AndroidManifest.xml`中添加必要权限: ```xml <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> <uses-permission android:name="android.permission.INSTALL_PACKAGES" tools:ignore="ProtectedPermissions" /> <!-- 需要系统签名 --> ``` 2. **安装会话正确配置** ```java PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); params.setInstallLocation(PackageHelper.INSTALL_LOCATION_AUTO); params.setInstallReason(PackageManager.INSTALL_REASON_USER); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { params.setInstallScenario(PackageManager.INSTALL_SCENARIO_FAST); } ``` 3. **特殊场景处理** *系统应用安装示例:* ```java // 需要系统签名 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { InstallLocationUtils.setInstallLocation( mContext.getPackageManager(), PackageHelper.INSTALL_LOCATION_PREFER_EXTERNAL); } ``` 4. **ADB命令增强权限** 对于调试安装,使用以下命令: ```bash adb install --bypass-low-target-sdk-block --force-queryable your_app.apk ``` --- #### 三、进阶调试技巧 1. **检查安装来源合法性** ```java // 在安装前添加来源验证 if (getPackageManager().canRequestPackageInstalls()) { // 执行安装逻辑 } else { // 引导用户开启「允许来自此来源的应用」 Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES); startActivityForResult(intent, REQUEST_CODE); } ``` 2. **系统服务调用验证** 检查`AppOpsManager`调用是否正常: ```java AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); if (appOps != null) { appOps.checkPackage(Binder.getCallingUid(), packageName); } ``` 避免出现引用[2]中的空指针异常 --- #### 四、兼容性处理方案 | 系统版本 | 关键适配点 | |---------|------------| | Android 7.0+ | 需要处理`FileProvider`路径授权 | | Android 8.0+ | 必须声明`REQUEST_INSTALL_PACKAGES`权限 | | Android 11+ | 强制使用`<queries>`声明交互包名 | | Android 13+ | 需要动态申请`POST_NOTIFICATIONS`权限 | --- #### 五、错误日志深度分析 当出现`SecurityException: install operation deny`时,建议按以下步骤排查: 1. 检查APK签名是否与已安装版本一致 2. 验证调用者UID是否具有安装权限: ```java if (Binder.getCallingUid() != Process.SHELL_UID && Binder.getCallingUid() != Process.ROOT_UID) { throw new SecurityException("Installation blocked"); } ``` 3. 确认未违反引用[3]中的用户权限限制策略
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值