免ROOT调用Android系统隐藏接口教程

免ROOT调用Android系统隐藏接口教程

现在随着各个安卓手机厂商逐渐封闭Bootloader,致使很多手机用户都不能自行解锁BL进行刷机定制或者root删掉部分无用内置了,只能被迫用答辩。

在这种行情之下,有一部分软件在不通过root的方式下,依旧可以达到替代部分系统功能,比如应用静默安装、卸载,后台清理等功能。

1、分析Shizuku、黑域、AppManager软件工作原理

Shizuku工作原理:

可以看到这部分源码,它是需要通过引导用户在adb shell环境下执行一个脚本,来激活一个后台服务,从而实现调用系统服务的功能。
从这部分源码来看,它是通过调用app_process命令来执行该Java程序的,它里面也有main函数,这是Java程序入口。

ServiceStarter.java


public class ServiceStarter {

    private static final String TAG = "ShizukuServiceStarter";

    private static final String EXTRA_BINDER = "moe.shizuku.privileged.api.intent.extra.BINDER";

    public static final String DEBUG_ARGS;

    static {
        int sdk = Build.VERSION.SDK_INT;
        if (sdk >= 30) {
            DEBUG_ARGS = "-Xcompiler-option" + " --debuggable" +
                    " -XjdwpProvider:adbconnection" +
                    " -XjdwpOptions:suspend=n,server=y";
        } else if (sdk >= 28) {
            DEBUG_ARGS = "-Xcompiler-option" + " --debuggable" +
                    " -XjdwpProvider:internal" +
                    " -XjdwpOptions:transport=dt_android_adb,suspend=n,server=y";
        } else {
            DEBUG_ARGS = "-Xcompiler-option" + " --debuggable" +
                    " -agentlib:jdwp=transport=dt_android_adb,suspend=n,server=y";
        }
    }

    private static final String USER_SERVICE_CMD_FORMAT = "(CLASSPATH='%s' %s%s /system/bin " +
            "--nice-name='%s' moe.shizuku.starter.ServiceStarter " +
            "--token='%s' --package='%s' --class='%s' --uid=%d%s)&";

    // DeathRecipient will automatically be unlinked when all references to the
    // binder is dropped, so we hold the reference here.
    @SuppressWarnings("FieldCanBeLocal")
    private static IBinder shizukuBinder;

    public static String commandForUserService(String appProcess, String managerApkPath, String token, String packageName, String classname, String processNameSuffix, int callingUid, boolean debug) {
        String processName = String.format("%s:%s", packageName, processNameSuffix);
        return String.format(Locale.ENGLISH, USER_SERVICE_CMD_FORMAT,
                managerApkPath, appProcess, debug ? (" " + DEBUG_ARGS) : "",
                processName,
                token, packageName, classname, callingUid, debug ? (" " + "--debug-name=" + processName) : "");
    }

    public static void main(String[] args) {
        if (Looper.getMainLooper() == null) {
            Looper.prepareMainLooper();
        }

        IBinder service;
        String token;

        UserService.setTag(TAG);
        Pair<IBinder, String> result = UserService.create(args);

        if (result == null) {
            System.exit(1);
            return;
        }

        service = result.first;
        token = result.second;

        if (!sendBinder(service, token)) {
            System.exit(1);
        }

        Looper.loop();
        System.exit(0);

        Log.i(TAG, "service exited");
    }
    ......
}

从这部分来看,可以更加清晰的了解到整个命令的参数拼接以及是如何执行的。

ShizukuUserServiceManager

@Override
    public String getUserServiceStartCmd(
            UserServiceRecord record, String key, String token, String packageName,
            String classname, String processNameSuffix, int callingUid, boolean use32Bits, boolean debug) {

        String appProcess = "/system/bin/app_process";
        if (use32Bits && new File("/system/bin/app_process32").exists()) {
            appProcess = "/system/bin/app_process32";
        }
        return ServiceStarter.commandForUserService(
                appProcess,
                ShizukuService.getManagerApplicationInfo().sourceDir,
                token, packageName, classname, processNameSuffix, callingUid, debug);
    }

这部分是用来监听服务是否启动的,这部分简单看一下都能看懂。

ApkChangedObservers.kt

package rikka.shizuku.server

import android.os.FileObserver
import android.util.Log
import java.io.File
import java.util.*

interface ApkChangedListener {
    fun onApkChanged()
}

private val observers = Collections.synchronizedMap(HashMap<String, ApkChangedObserver>())

object ApkChangedObservers {

    @JvmStatic
    fun start(apkPath: String, listener: ApkChangedListener) {
        // inotify watchs inode, if the there are still processes holds the file, DELTE_SELF will not be triggered
        // so we need to watch the parent folder

        val path = File(apkPath).parent!!
        val observer = observers.getOrPut(path) {
            ApkChangedObserver(path).apply {
                startWatching()
            }
        }
        observer.addListener(listener)
    }

    @JvmStatic
    fun stop(listener: ApkChangedListener) {
        val pathToRemove = mutableListOf<String>()

        for ((path, observer) in observers) {
            observer.removeListener(listener)

            if (!observer.hasListeners()) {
                pathToRemove.add(path)
            }
        }

        for (path in pathToRemove) {
            observers.remove(path)?.stopWatching()
        }
    }
}

class ApkChangedObserver(private val path: String) : FileObserver(path, DELETE) {

    private val listeners = mutableSetOf<ApkChangedListener>()

    fun addListener(listener: ApkChangedListener): Boolean {
        return listeners.add(listener)
    }

    fun removeListener(listener: ApkChangedListener): Boolean {
        return listeners.remove(listener)
    }

    fun hasListeners(): Boolean {
        return listeners.isNotEmpty()
    }

    override fun onEvent(event: Int, path: String?) {
        Log.d("ShizukuServer", "onEvent: ${eventToString(event)} $path")

        if ((event and 0x00008000 /* IN_IGNORED */) != 0 || path == null) {
            return
        }

        if (path == "base.apk") {
            stopWatching()
            ArrayList(listeners).forEach { it.onApkChanged() }
        }
    }

    override fun startWatching() {
        super.startWatching()
        Log.d("ShizukuServer", "start watching $path")
    }

    override fun stopWatching() {
        super.stopWatching()
        Log.d("ShizukuServer", "stop watching $path")
    }
}

private fun eventToString(event: Int): String {
    val sb = StringBuilder()
    if (event and FileObserver.ACCESS == FileObserver.ACCESS) {
        sb.append("ACCESS").append(" | ")
    }
    if (event and FileObserver.MODIFY == FileObserver.MODIFY) {
        sb.append("MODIFY").append(" | ")
    }
    if (event and FileObserver.ATTRIB == FileObserver.ATTRIB) {
        sb.append("ATTRIB").append(" | ")
    }
    if (event and FileObserver.CLOSE_WRITE == FileObserver.CLOSE_WRITE) {
        sb.append("CLOSE_WRITE").append(" | ")
    }
    if (event and FileObserver.CLOSE_NOWRITE == FileObserver.CLOSE_NOWRITE) {
        sb.append("CLOSE_NOWRITE").append(" | ")
    }
    if (event and FileObserver.OPEN == FileObserver.OPEN) {
        sb.append("OPEN").append(" | ")
    }
    if (event and FileObserver.MOVED_FROM == FileObserver.MOVED_FROM) {
        sb.append("MOVED_FROM").append(" | ")
    }
    if (event and FileObserver.MOVED_TO == FileObserver.MOVED_TO) {
        sb.append("MOVED_TO").append(" | ")
    }
    if (event and FileObserver.CREATE == FileObserver.CREATE) {
        sb.append("CREATE").append(" | ")
    }
    if (event and FileObserver.DELETE == FileObserver.DELETE) {
        sb.append("DELETE").append(" | ")
    }
    if (event and FileObserver.DELETE_SELF == FileObserver.DELETE_SELF) {
        sb.append("DELETE_SELF").append(" | ")
    }
    if (event and FileObserver.MOVE_SELF == FileObserver.MOVE_SELF) {
        sb.append("MOVE_SELF").append(" | ")
    }

    if (event and 0x00008000 == 0x00008000) {
        sb.append("IN_IGNORED").append(" | ")
    }

    if (event and 0x40000000 == 0x40000000) {
        sb.append("IN_ISDIR").append(" | ")
    }

    return if (sb.isNotEmpty()) {
        sb.substring(0, sb.length - 3)
    } else {
        sb.toString()
    }
}

黑阈工作原理:
由于黑域仅公开了早期版本源码,后面新版本,可能会有所不同。

直接看这部分源码实现,黑域是用c语言实现一个可执行文件,用来执行这个服务:me.piebridge.brevent.server.BreventServer

brevent.c

static int worker() {
    FILE *file;
    char *path;
    char line[PATH_MAX];
    char classpath[PATH_MAX];
    char *arg[] = {"app_process", "/system/bin", "--nice-name=brevent_server",
                   "me.piebridge.brevent.server.BreventServer",
                   STR(SIGUSR1), BREVENT, start, NULL};
    file = popen("pm path " BREVENT, "r");
    if (file != NULL) {
        memset(line, 0, PATH_MAX);
        fgets(line, sizeof(line), file);
        LOGD("line: %s", line);
        rstrip(line);
        pclose(file);
    } else {
        LOGE("can't exec pm path");
        return -1;
    }
    path = strchr(line, ':');
    if (path == NULL) {
        LOGE("can't get path");
        return -1;
    }
    path++;
    LOGD("loader path: %s", path);
    if (access(path, F_OK) == -1) {
        LOGE("can't find loader: %s", path);
        return -1;
    }
    pid_t pid = fork();
    switch (pid) {
        case -1:
            LOGE("cannot fork");
            return -1;
        case 0:
            break;
        default:
            return pid;
    }
    memset(classpath, 0, PATH_MAX);
    sprintf(classpath, "CLASSPATH=%s", path);
    putenv(classpath);
    LOGD("start: %s", start);
    return execvp(arg[0], arg);
}

通过激活这个服务进程,可以达到实现对手机上应用设置待机、打盹模式、强制终止等功能。
它通过引导用户使用adb shell执行该程序激活一个后台服务,然后调用系统隐藏接口来实现上述功能。

AppManager工作原理:

AppManager跟Rikka做的AppOps类似,这两个都是收费的,AppManager是开源的。

还是直接看源码,这个开源软件做的比较直接明了,没上面两个那么弯弯绕,通过shell脚本,可以更加直观的看到具体启动过程。

run_server.sh

#!/system/bin/sh
# SPDX-License-Identifier: GPL-3.0-or-later

if [[ $# -lt 2 ]]; then
    echo "USAGE: ./run_server.sh <path|port> <token>"
    exit 1
fi

SERVER_NAME=
JAR_NAME=
JAR_PATH=
%ENV_VARS%
PORT="path:$1"
TOKEN=",token:$2"
ARGS="${PORT}${ARGS}${TOKEN}"
JAR_PACKAGE_NAME="io.github.muntashirakon.AppManager"
JAR_MAIN_CLASS="${JAR_PACKAGE_NAME}.server.ServerRunner"
TMP_PATH="/data/local/tmp"
EXEC_JAR_PATH=${TMP_PATH}/${JAR_NAME}

echo "Starting $SERVER_NAME..."
# Copy am.jar to executable directory
cp -f ${JAR_PATH} ${EXEC_JAR_PATH}
if [[ $? -ne 0 ]]; then
    # Copy failed
    echo "Error! Could not copy jar file to the executable directory."
    exit 1
fi
# Fix permission
chmod 755 ${EXEC_JAR_PATH}
chown shell:shell ${EXEC_JAR_PATH}
# Debug log
echo "Jar path: $JAR_PATH"
echo "Args: $ARGS"
# Save jar path to environment variable
export CLASSPATH=${EXEC_JAR_PATH}
# Execute local server
exec app_process /system/bin --nice-name=${SERVER_NAME} ${JAR_MAIN_CLASS} "$ARGS" $@  &
if [[ $? -ne 0 ]]; then
    # Start failed
    echo "Error! Could not start local server."
    exit 1
else
    # Start success
    echo "Local server has started."
    echo "Use Ctrl+C to exit."
fi
# Print pid
ps | grep ${SERVER_NAME}
exit 0

核心部分:

# Execute local server
exec app_process /system/bin --nice-name=${SERVER_NAME} ${JAR_MAIN_CLASS} "$ARGS" $@  &

后边跟了个&符号,是后台执行的意思。

为什么要把这个也加入进来?因为它调用并封装了大量的隐藏接口,而且是支持Android4.x至Android14的,涵盖范围很广。
部分源码

ActivityManagerCompat.java#L159

    @SuppressWarnings("deprecation")
    public static IActivityManager getActivityManager() {
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            return IActivityManager.Stub.asInterface(ProxyBinder.getService(Context.ACTIVITY_SERVICE));
        } else {
            return ActivityManagerNative.asInterface(ProxyBinder.getService(Context.ACTIVITY_SERVICE));
        }
    }

 @Nullable
    public static IContentProvider getContentProviderExternal(String name, int userId, IBinder token, String tag)
            throws RemoteException {
        IActivityManager am = getActivityManager();
        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                return am.getContentProviderExternal(name, userId, token, tag).provider;
            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                return ((android.app.ContentProviderHolder) am.getContentProviderExternal(name, userId, token)).provider;
            } else {
                return ((IActivityManager.ContentProviderHolder) am.getContentProviderExternal(name, userId, token)).provider;
            }
        } catch (NullPointerException e) {
            return null;
        }
    }




2、分析Android Framework里面部分命令源码

通过上面对主流常用的免ROOT软件有了个简单的了解后,可以开始分析Android Framework源码了。

这里不对源码进行深度的分析,仅分析几个命令参数功能的源码以及各个Android版本的变动。

am命令

am force-stop pkgname

Android 7.0以下源码实现:
Am.java#L740

 private void runForceStop() throws Exception {
        int userId = UserHandle.USER_ALL;

        String opt;
        while ((opt=nextOption()) != null) {
            if (opt.equals("--user")) {
                userId = parseUserArg(nextArgRequired());
            } else {
                System.err.println("Error: Unknown option: " + opt);
                return;
            }
        }
        mAm.forceStopPackage(nextArgRequired(), userId);
    }

Android8.0以上源码实现:
Am.java#L182

  void runAmCmd(String[] args) throws AndroidException {
        final MyShellCallback cb = new MyShellCallback();
        try {
            mAm.asBinder().shellCommand(FileDescriptor.in, FileDescriptor.out, FileDescriptor.err,
                    args, cb, new ResultReceiver(null) { });
        } catch (RemoteException e) {
            System.err.println(NO_SYSTEM_ERROR_CODE);
            throw new AndroidException("Can't call activity manager; is the system running?");
        } finally {
            cb.mActive = false;
        }
    }

最终实现都是调用IActivityManager.forceStopPackage这个函数:
IActivityManager.aidl#L211

	boolean clearApplicationUserData(in String packageName,
            in IPackageDataObserver observer, int userId);
    void forceStopPackage(in String packageName, int userId);//最终调用这个函数
    boolean killPids(in int[] pids, in String reason, boolean secure);

IActivityManager对象获取
Android7.0以下源码:
ActivityManagerNative.java#L3047


   /**
     * Retrieve the system's default/global activity manager.
     */
    static public IActivityManager getDefault() {
        return gDefault.get();
    }

private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager", "default service binder = " + b);
            }
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager", "default service = " + am);
            }
            return am;
        }
    };


Android8.0以上源码:
ActivityManager.java#L4216

 /**
     * @hide
     */
    public static IActivityManager getService() {
        return IActivityManagerSingleton.get();
    }

    private static final Singleton<IActivityManager> IActivityManagerSingleton =
            new Singleton<IActivityManager>() {
                @Override
                protected IActivityManager create() {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                    final IActivityManager am = IActivityManager.Stub.asInterface(b);
                    return am;
                }
            };

pm命令

pm uninstall packageName

Android4.4源码实现:
Pm.java#L1093

private void runUninstall() {
        int unInstallFlags = PackageManager.DELETE_ALL_USERS;

        String opt;
        while ((opt=nextOption()) != null) {
            if (opt.equals("-k")) {
                unInstallFlags |= PackageManager.DELETE_KEEP_DATA;
            } else {
                System.err.println("Error: Unknown option: " + opt);
                return;
            }
        }

        String pkg = nextArg();
        if (pkg == null) {
            System.err.println("Error: no package specified");
            showUsage();
            return;
        }
        boolean result = deletePackage(pkg, unInstallFlags);
        if (result) {
            System.out.println("Success");
        } else {
            System.out.println("Failure");
        }
    }

    private boolean deletePackage(String pkg, int unInstallFlags) {
        PackageDeleteObserver obs = new PackageDeleteObserver();
        try {
            mPm.deletePackageAsUser(pkg, obs, UserHandle.USER_OWNER, unInstallFlags);

            synchronized (obs) {
                while (!obs.finished) {
                    try {
                        obs.wait();
                    } catch (InterruptedException e) {
                    }
                }
            }
        } catch (RemoteException e) {
            System.err.println(e.toString());
            System.err.println(PM_NOT_RUNNING_ERR);
        }
        return obs.result;
    }

Android5.x源码实现:
Pm.java#L1322

private void runUninstall() throws RemoteException {
        int flags = 0;
        int userId = UserHandle.USER_ALL;

        String opt;
        while ((opt=nextOption()) != null) {
            if (opt.equals("-k")) {
                flags |= PackageManager.DELETE_KEEP_DATA;
            } else if (opt.equals("--user")) {
                String param = nextArg();
                if (isNumber(param)) {
                    userId = Integer.parseInt(param);
                } else {
                    showUsage();
                    System.err.println("Error: Invalid user: " + param);
                    return;
                }
            } else {
                System.err.println("Error: Unknown option: " + opt);
                return;
            }
        }

        String pkg = nextArg();
        if (pkg == null) {
            System.err.println("Error: no package specified");
            showUsage();
            return;
        }

        if (userId == UserHandle.USER_ALL) {
            userId = UserHandle.USER_OWNER;
            flags |= PackageManager.DELETE_ALL_USERS;
        } else {
            PackageInfo info;
            try {
                info = mPm.getPackageInfo(pkg, 0, userId);
            } catch (RemoteException e) {
                System.err.println(e.toString());
                System.err.println(PM_NOT_RUNNING_ERR);
                return;
            }
            if (info == null) {
                System.err.println("Failure - not installed for " + userId);
                return;
            }
            final boolean isSystem =
                    (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
            // If we are being asked to delete a system app for just one
            // user set flag so it disables rather than reverting to system
            // version of the app.
            if (isSystem) {
                flags |= PackageManager.DELETE_SYSTEM_APP;
            }
        }

        final LocalIntentReceiver receiver = new LocalIntentReceiver();
        mInstaller.uninstall(pkg, flags, receiver.getIntentSender(), userId);

        final Intent result = receiver.getResult();
        final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
                PackageInstaller.STATUS_FAILURE);
        if (status == PackageInstaller.STATUS_SUCCESS) {
            System.out.println("Success");
        } else {
            Log.e(TAG, "Failure details: " + result.getExtras());
            System.out.println("Failure ["
                    + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
        }
    }

Android6.x源码实现:
Pm.java#L1578

private int runUninstall() throws RemoteException {
        int flags = 0;
        int userId = UserHandle.USER_ALL;

        String opt;
        while ((opt=nextOption()) != null) {
            if (opt.equals("-k")) {
                flags |= PackageManager.DELETE_KEEP_DATA;
            } else if (opt.equals("--user")) {
                String param = nextArg();
                if (isNumber(param)) {
                    userId = Integer.parseInt(param);
                } else {
                    showUsage();
                    System.err.println("Error: Invalid user: " + param);
                    return 1;
                }
            } else {
                System.err.println("Error: Unknown option: " + opt);
                return 1;
            }
        }

        String pkg = nextArg();
        if (pkg == null) {
            System.err.println("Error: no package specified");
            showUsage();
            return 1;
        }

        if (userId == UserHandle.USER_ALL) {
            userId = UserHandle.USER_OWNER;
            flags |= PackageManager.DELETE_ALL_USERS;
        } else {
            PackageInfo info;
            try {
                info = mPm.getPackageInfo(pkg, 0, userId);
            } catch (RemoteException e) {
                System.err.println(e.toString());
                System.err.println(PM_NOT_RUNNING_ERR);
                return 1;
            }
            if (info == null) {
                System.err.println("Failure - not installed for " + userId);
                return 1;
            }
            final boolean isSystem =
                    (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
            // If we are being asked to delete a system app for just one
            // user set flag so it disables rather than reverting to system
            // version of the app.
            if (isSystem) {
                flags |= PackageManager.DELETE_SYSTEM_APP;
            }
        }

        final LocalIntentReceiver receiver = new LocalIntentReceiver();
        mInstaller.uninstall(pkg, null /* callerPackageName */, flags,
                receiver.getIntentSender(), userId);

        final Intent result = receiver.getResult();
        final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
                PackageInstaller.STATUS_FAILURE);
        if (status == PackageInstaller.STATUS_SUCCESS) {
            System.out.println("Success");
            return 0;
        } else {
            Log.e(TAG, "Failure details: " + result.getExtras());
            System.err.println("Failure ["
                    + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
            return 1;
        }
    }

Android7以上源码实现:
Pm.java#L643

    private int runUninstall() {
        return runShellCommand("package", mArgs);
    }

Android10以后,源码位置变更了。
PackageManagerShellCommand.java#L1701

   private int runUninstall() throws RemoteException {
        final PrintWriter pw = getOutPrintWriter();
        int flags = 0;
        int userId = UserHandle.USER_ALL;
        long versionCode = PackageManager.VERSION_CODE_HIGHEST;

        String opt;
        while ((opt = getNextOption()) != null) {
            switch (opt) {
                case "-k":
                    flags |= PackageManager.DELETE_KEEP_DATA;
                    break;
                case "--user":
                    userId = UserHandle.parseUserArg(getNextArgRequired());
                    break;
                case "--versionCode":
                    versionCode = Long.parseLong(getNextArgRequired());
                    break;
                default:
                    pw.println("Error: Unknown option: " + opt);
                    return 1;
            }
        }

        final String packageName = getNextArg();
        if (packageName == null) {
            pw.println("Error: package name not specified");
            return 1;
        }

        // if a split is specified, just remove it and not the whole package
        final String splitName = getNextArg();
        if (splitName != null) {
            return runRemoveSplit(packageName, splitName);
        }

        userId = translateUserId(userId, true /*allowAll*/, "runUninstall");
        final LocalIntentReceiver receiver = new LocalIntentReceiver();
        PackageManagerInternal internal = LocalServices.getService(PackageManagerInternal.class);

        if (internal.isApexPackage(packageName)) {
            internal.uninstallApex(packageName, versionCode, userId, receiver.getIntentSender());
        } else {
            if (userId == UserHandle.USER_ALL) {
                userId = UserHandle.USER_SYSTEM;
                flags |= PackageManager.DELETE_ALL_USERS;
            } else {
                final PackageInfo info = mInterface.getPackageInfo(packageName,
                        PackageManager.MATCH_STATIC_SHARED_LIBRARIES, userId);
                if (info == null) {
                    pw.println("Failure [not installed for " + userId + "]");
                    return 1;
                }
                final boolean isSystem =
                        (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
                // If we are being asked to delete a system app for just one
                // user set flag so it disables rather than reverting to system
                // version of the app.
                if (isSystem) {
                    flags |= PackageManager.DELETE_SYSTEM_APP;
                }
            }

            mInterface.getPackageInstaller().uninstall(new VersionedPackage(packageName,
                            versionCode), null /*callerPackageName*/, flags,
                    receiver.getIntentSender(), userId);
        }

        final Intent result = receiver.getResult();
        final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
                PackageInstaller.STATUS_FAILURE);
        if (status == PackageInstaller.STATUS_SUCCESS) {
            pw.println("Success");
            return 0;
        } else {
            pw.println("Failure ["
                    + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
            return 1;
        }
    }

最终实现:
Android4.4是调用IPackageManager.deletePackageAsUser函数实现
IPackageManager.aidl#L209

    /**
     * Delete a package for a specific user.
     *
     * @param packageName The fully qualified name of the package to delete.
     * @param observer a callback to use to notify when the package deletion in finished.
     * @param userId the id of the user for whom to delete the package
     * @param flags - possible values: {@link #DONT_DELETE_DATA}
     */
    void deletePackageAsUser(in String packageName, IPackageDeleteObserver observer,
            int userId, int flags);

    String getInstallerPackageName(in String packageName);

Android5以上是调用IPackageInstaller.uninstall函数实现
IPackageInstaller.aidl#L47

  void unregisterCallback(IPackageInstallerCallback callback);

    void uninstall(String packageName, int flags, in IntentSender statusReceiver, int userId);

这里就简单先分析了两个命令的功能,其它的就不一一分析了,由于篇幅的问题,只能交给你们自己去分析了。

3、Android跨进程通信简介

进程间通信简称IPC,英文为 Inter Process Communication ,在操作系统中,线程是CPU调度的基本单元,进程是操作系统调度的基本单元。在Android中一个进程一般指一个应用程序,可以包含多个线程,但最少有一个线程,在Android是主线程。

详情可以看别人写的

4、代码示例
通过上面对应用的分析与源码介绍,可以知道大概的流程了,如果我们要实现一个停止后台应用进程的功能,按照以往传统做法,我们除了在adb shell下调用

am force-stop pkgname

这个命令外,那么就只能通过获取root来调用这个命令实现了,而在以上几个项目成品源码的分析下,可以得知,通过adb shell调用app_process命令,启动一个Java后台进程,该Java进程即拥有adb shell一样的PID,它的级别与shell一样,也可调用am、pm、appops这些命令,且不再需要授权,只需要该服务在后台运行即可。

但是这样做的话,效率就太低下了,每一次运行一次命令就要重新创建一次shell对象,因为它即使是shell级别,但是调用命令,还是会调用一次app_process或者cmd命令,这跟上述的软件实现完全不一样,那么我们如果想要调用系统的api该怎么做?

通过以上软件的源码分析,我们可以知道,它们为了能够顺利调用系统api接口,在本地也创建了很多跟那些隐藏接口一样的文件跟对外接口函数,我们在分析Android Framework源码的时候,也发现了一个问题,里面那些接口,大部分都是@hide属性的,这部分我们是不可以调用的,但是adb shell环境下是可以使用的,这意味着,我们也仿照上述软件创建对应的文件跟编写对外接口函数名字,是否也可以达到同样效果?

实现思路:

  1. 编写一个Java应用程序入口
  2. 在这个Java应用程序里面,实现对应的功能
  3. 创建对应的系统接口文件
  4. 在Java应用程序里面编写一个socket服务端,用来接收提交过来的动态参数
  5. 编写一个客户端,用于提交对应的包名与其它动态参数
  6. 编写一个基于app_process的启动脚本,用于启动该Java程序

思路大概就这样,我这里已经写好了,就不给你们仔细分析了,你们直接看成品源码吧。

app_process启动的服务端源码:

package com.easymanager.mylife;

import android.os.Looper;
import android.os.Process;

import com.easymanager.core.entity.TransmissionEntity;
import com.easymanager.core.entity.easyManagerClientEntity;
import com.easymanager.core.server.easyManagerAPI;
import com.easymanager.core.utils.CMD;
import com.easymanager.enums.easyManagerEnums;

public class startAdbService {

    private final static easyManagerAPI managerAPI = new easyManagerAPI();

    public static void main(String[] args) {
        // 利用looper让线程循环
        Looper.prepareMainLooper();
        new Thread(new Runnable() {
            @Override
            public void run() {
                new adbService(new adbService.SocketListener() {
                    @Override
                    public CMD sendCMD(String cmdstr, boolean isRoot) {
                        if(cmdstr != null){
                            return new CMD(cmdstr,managerAPI.isRoot());
                        }
                        return null;
                    }

                    @Override
                    public Object doSomeThing(easyManagerClientEntity adbEntity2) {
                        if(adbEntity2 != null && adbEntity2.getCmdstr() == null){
                            TransmissionEntity entity = adbEntity2.getTransmissionEntity();
                            if(entity != null && entity.getRequestpkg() != null ){
                                if(managerAPI.getGrantUserState(entity.getRequestpkg()) == 0 || entity.getRequestpkg().equals("com.easymanager")){
                                    switch (adbEntity2.getEasyManagerMode()){
                                        case easyManagerEnums.IS_ROOT:
                                            return managerAPI.isRoot();
                                        case easyManagerEnums.IS_ADB:
                                            return managerAPI.isADB();
                                        case easyManagerEnums.GET_SYSTEM_SERVICE:
                                            return managerAPI.getSystemService(entity.getPkgname());
                                        case easyManagerEnums.KILL_PROCESS:
                                            if(managerAPI.isRoot() || managerAPI.isADB()){
                                                managerAPI.killpkg(entity.getPkgname());
                                            }else {
                                                return new String("not permisson use !! please active adb or root mode !!");
                                            }
                                            break;
                                        case easyManagerEnums.SET_APPOPS:
                                            if(managerAPI.isRoot() || managerAPI.isADB()){
                                                managerAPI.setAppopsMode(entity);
                                            }else {
                                                return new String("not permisson use !! please active adb or root mode !!");
                                            }
                                            break;
                                        case easyManagerEnums.INSTALL_APK:
                                            if(managerAPI.isRoot() || managerAPI.isADB()){
                                                managerAPI.installAPK(entity.getPkgname());
                                            }else {
                                                return new String("not permisson use !! please active adb or root mode !!");
                                            }
                                            break;
                                        case easyManagerEnums.UNINSTALL_APK:
                                            if(managerAPI.isRoot() || managerAPI.isADB()){
                                                managerAPI.uninstallApp(entity.getPkgname());
                                            }else {
                                                return new String("not permisson use !! please active adb or root mode !!");
                                            }
                                            break;
                                        case easyManagerEnums.SET_COMPONENT_OR_PACKAGE_ENABLE_STATE:
                                            if(managerAPI.isRoot()){
                                                managerAPI.setComponentOrPackageEnabledState(entity.getPkgname(),entity.getOpsmode());
                                            }else {
                                                return new String("not permisson use !! please active root mode !!");
                                            }
                                            break;
                                        case easyManagerEnums.SET_PACKAGE_HIDE_STATE:
                                            if(managerAPI.isRoot()){
                                                managerAPI.setPackageHideState(entity.getPkgname(),entity.getOpsmode()==0);
                                            }else {
                                                return new String("not permisson use !! please active root mode !!");
                                            }
                                            break;
                                        
                                        case easyManagerEnums.GET_SERVER_STATUS:
                                            return true;
                                        case easyManagerEnums.GET_GRANT_USERS:
                                            return managerAPI.getGrantUsers();
                                        
                                    }
                                }else {
                                    if(adbEntity2.getEasyManagerMode() == easyManagerEnums.GRANT_USER){
                                        managerAPI.requestGrantUserState(entity.getRequestpkg());
                                    } else {
                                        return  new String("the " + entity.getRequestpkg() + " has not permission use the val .");
                                    }
                                }

                            }else if(adbEntity2.getEasyManagerMode() == easyManagerEnums.IS_ADB){
                                return managerAPI.isADB();
                            }else if(adbEntity2.getEasyManagerMode() == easyManagerEnums.IS_ROOT){
                                return managerAPI.isRoot();
                            }else if(adbEntity2.getEasyManagerMode() == easyManagerEnums.GET_SERVER_STATUS){
                                return true;
                            }else {
                                return new String("please put requestpkg val .");
                            }
                        }

                        return new String(Process.myUid()+" --- " + Process.myUserHandle().hashCode());
                    }
                });

            }
        }).start();
        Looper.loop();
    }

}

服务端源码:

package com.easymanager.mylife;

import com.easymanager.core.entity.easyManagerClientEntity;
import com.easymanager.core.entity.easyManagerServiceEntity;
import com.easymanager.core.utils.CMD;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class adbService {

    public final static int PORT = 56997;
    private SocketListener listener;

    public adbService(SocketListener listener) {
        this.listener = listener;
        try {
            // 利用ServerSocket类启动服务,然后指定一个端口
            ServerSocket serverSocket = new ServerSocket(PORT);
            System.out.println("easyManager adb server running " + PORT + " port");
            ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(10);
            // 新建一个线程池用来并发处理客户端的消息
            ThreadPoolExecutor executor = new ThreadPoolExecutor(
                    5,
                    10,
                    50000,
                    TimeUnit.MILLISECONDS,
                    queue
            );
            while (true) {
                Socket socket = serverSocket.accept();
                // 接收到新消息
                executor.execute(new processMsg(socket));
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("SocketServer create Exception:" + e.toString());
        }
    }

    class processMsg implements Runnable {
        Socket socket;

        CMD cmd;
        Object o;

        public processMsg(Socket s) {
            socket = s;
        }

        public void run() {
            try {
                // 通过流读取内容
                ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                easyManagerClientEntity adbee2 = (easyManagerClientEntity) ois.readObject();
                cmd = listener.sendCMD(adbee2.getCmdstr(),false);
                o = listener.doSomeThing(adbee2);
                ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                easyManagerServiceEntity a = new easyManagerServiceEntity(cmd,o);
                oos.writeObject(a);
                oos.flush();
                oos.close();
                socket.close();
                //需要实现appops的功能
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("socket connection error:" + e.getStackTrace().toString());
            }
        }
    }

    public interface SocketListener{

        CMD sendCMD(String cmdstr,boolean isRoot);

        Object doSomeThing(easyManagerClientEntity adbEntity2);

    }


}

客户端源码:

package com.easymanager.mylife;

import com.easymanager.core.entity.easyManagerClientEntity;
import com.easymanager.core.entity.easyManagerServiceEntity;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;

public class adbClient {

    private final int PORT = 56997;
    private SocketListener listener;

    private easyManagerServiceEntity adbEn;

    public adbClient(easyManagerClientEntity adee2, SocketListener listener) {
        this.listener = listener;
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                Socket socket = new Socket();
                ObjectOutputStream oos;
                try {
                    socket.connect(new InetSocketAddress("127.0.0.1", PORT));
                    oos = new ObjectOutputStream(socket.getOutputStream());
                    // 发送指令
                    oos.writeObject(adee2);
                    oos.flush();

                    // 读取服务端返回
                    readServerData(socket,oos);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private synchronized void readServerData(final Socket socket,ObjectOutputStream oos) {
        try {
            ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
//            cmd = (CMD)ois.readObject();
            adbEn = (easyManagerServiceEntity) ois.readObject();
            ois.close();
            oos.close();
            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public easyManagerServiceEntity getAdbEntity(){
        return adbEn;
    }

    public interface SocketListener {

        easyManagerServiceEntity getAdbEntity(easyManagerServiceEntity adfb);
    }

}

调用客户端源码的地方:

package com.easymanager.utils;

import android.content.Context;
import android.os.IBinder;

import com.easymanager.core.entity.TransmissionEntity;
import com.easymanager.core.entity.easyManagerClientEntity;
import com.easymanager.core.entity.easyManagerServiceEntity;
import com.easymanager.core.utils.CMD;
import com.easymanager.core.utils.FileUtils;
import com.easymanager.enums.easyManagerEnums;
import com.easymanager.mylife.adbClient;
import com.easymanager.mylife.startAdbService;

import java.util.HashMap;

public class easyManagerUtils {

    public easyManagerServiceEntity putOptionOnServer(easyManagerClientEntity adben2){
        adbClient ac = new adbClient(adben2, new adbClient.SocketListener() {
            @Override
            public easyManagerServiceEntity getAdbEntity(easyManagerServiceEntity adfb) {
                return null;
            }
        });
        return ac.getAdbEntity();
    }

    public void activeRoot(Context context) {
        String cmdstr ="killall EAMADB\n" +
                "exec app_process -Djava.class.path=\""+context.getApplicationInfo().sourceDir+"\" /system/bin --nice-name=EAMADB "+ startAdbService.class.getName()+" >>/dev/null 2>&1 &\n" +
                "echo \"run EAMADB ok [root]\"";
        CMD cmd = new CMD(cmdstr);
    }

    public void dead(boolean isRoot){
        String cmdstr = "killall EAMADB;";
        if(isROOT() || isADB()){
            try {
                runCMD(cmdstr);
            }catch (Exception e){

            }
        }

        if(isRoot){
            new CMD(cmdstr);
        }
    }


    public boolean isADB(){
        easyManagerClientEntity adben2 = new easyManagerClientEntity(null,null,easyManagerEnums.IS_ADB);
        return checkBool(adben2);
    }

    public boolean isROOT(){
        easyManagerClientEntity adben2 = new easyManagerClientEntity(null,null,easyManagerEnums.IS_ROOT);
        return checkBool(adben2);
    }

    public IBinder getSystemServer(String serverName,Context context){
        easyManagerClientEntity adben2 = new easyManagerClientEntity(null,new TransmissionEntity(serverName,null,context.getPackageName(),0),easyManagerEnums.GET_SYSTEM_SERVICE);
        return (IBinder) putOptionOnServer(adben2).getObject();
    }

    public boolean checkBool(easyManagerClientEntity adben2){
        easyManagerServiceEntity serviceEntity = putOptionOnServer(adben2);
        boolean a = false;
        try{
            a =  (boolean) serviceEntity.getObject();
        }catch (Exception e){
            e.printStackTrace();
        }
        return a;
    }

    public CMD runCMD(String cmdstr){
        easyManagerClientEntity adben2 = new easyManagerClientEntity(cmdstr,null,-1);
        easyManagerServiceEntity eee = putOptionOnServer(adben2);
        return eee.getCmd();
    }

    public void killpkg(TransmissionEntity entity){
        easyManagerClientEntity adben2 = new easyManagerClientEntity(null,entity,easyManagerEnums.KILL_PROCESS);
        easyManagerServiceEntity eee = putOptionOnServer(adben2);
    }

    public void setAppopsMode(TransmissionEntity entity){
        easyManagerClientEntity adben2 = new easyManagerClientEntity(null,entity,easyManagerEnums.SET_APPOPS);
        easyManagerServiceEntity eee = putOptionOnServer(adben2);
    }

    public void setAppopsModeCore(TransmissionEntity entity){
        easyManagerClientEntity adben2 = new easyManagerClientEntity(null,entity,easyManagerEnums.SET_APPOPS_CORE);
        easyManagerServiceEntity eee = putOptionOnServer(adben2);
    }

    public void installAPK(TransmissionEntity entity){
        easyManagerClientEntity adben2 = new easyManagerClientEntity(null,entity,easyManagerEnums.INSTALL_APK);
        easyManagerServiceEntity eee = putOptionOnServer(adben2);
    }

    public void uninstallAPK(TransmissionEntity entity){
        easyManagerClientEntity adben2 = new easyManagerClientEntity(null,entity,easyManagerEnums.UNINSTALL_APK);
        easyManagerServiceEntity eee = putOptionOnServer(adben2);
    }

    public void setComponentOrPackageEnabledState(TransmissionEntity entity){
        easyManagerClientEntity adben2 = new easyManagerClientEntity(null,entity,easyManagerEnums.SET_COMPONENT_OR_PACKAGE_ENABLE_STATE);
        easyManagerServiceEntity eee = putOptionOnServer(adben2);
    }

    public void setPackageHideState(TransmissionEntity entity){
        easyManagerClientEntity adben2 = new easyManagerClientEntity(null,entity,easyManagerEnums.SET_PACKAGE_HIDE_STATE);
        easyManagerServiceEntity eee = putOptionOnServer(adben2);
    }


    public void setFirewallState(int pkguid , boolean isEnable){
        String  disable_cmd = "iptables -I OUTPUT -m owner --uid-owner "+pkguid+" -j DROP";
        String  enable_cmd = "iptables -I OUTPUT -m owner --uid-owner "+pkguid+" -j ACCEPT";
        CMD cmd = runCMD(isEnable ? enable_cmd : disable_cmd);
    }

    public void dumpAPK(String apksourcepath,String outpath){
        FileUtils fu = new FileUtils();
        fu.copyFile(apksourcepath,outpath);
    }


    public Boolean getServerStatus(){
        TransmissionEntity entity = new TransmissionEntity(null,null,null,1);
        easyManagerClientEntity adben2 = new easyManagerClientEntity(null,entity,easyManagerEnums.GET_SERVER_STATUS);
        return checkBool(adben2);
    }


}

所有实现的功能,都需要在服务端里面调用跟执行才能生效。

5、demo项目开源地址
完整项目开源地址
对应的开发工具包SDK
SDK成品下载地址
项目成品安装包下载地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值