一、题目介绍
项目所有内容均需基于AOSP原版代码实现,版本可选择10或者11,测试设备建议使用Pixel3. 1、修改su代码,并实现root管理APK,功能至少包括APP申请root权限管理、root权限请求记录; 2、实现USB调试功能一键开关,WiFi一键开关; 3、实现AOSP系统内置指定WiFi名称与密码,刷机后可自动连接指定WiFi; 4、(可选)为AOSP11版本系统添加OpenSSH Server;
二、环境介绍
pixel3测试机一台 aosp11源代码(android-11.0.0_r1) Android studio
三、操作步骤与程序设计
aosp源码下载
我们将Google的镜像地址替换为清华的地址https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest之后,下载的速度会更快一些。 首先找到一个空目录
mkdir android11
cd android11
repo init -u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest
repo init --depth 1 -u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest -b android-11.0.1_r1
repo sync -j20
接着我们就进入漫长的下载等待时间,如果速度能达到10m/s的话,估计要等待3-5个小时。
pixel3 硬件库下载
Android11刷机不能缺乏硬件库,否则刷机后会不断重启,首先我们要查看对应的BUILD_ID和pixel3的代号,查看源码中的build/core/build_id.mk文件,其中BUILD_ID=RP1A.200720.009,pixel3的代号是blueline,对应的硬件库下载地址是: https://developers.google.cn/android/drivers?hl=zh-cn#bluelinerp1a.200720.009 将两个文件下载到源码根目录,然后执行,会自动生成vendor文件
~/code/android11$ tar zxvf google_devices-blueline-rp1a.200720.009-6cd41940.tgz
~/code/android11$ tar zxvf qcom-blueline-rp1a.200720.009-f772c38c.tgz
~/code/android11$ ./extract-google_devices-blueline.sh
~/code/android11$ ./extract-qcom-blueline.sh
这里补充一下,Google的每一代手机都有一个对应的代号,具体如下图:
编译aosp11源码
首先我们需要安装openjdk8
sudo apt-get update
sudo apt-get install openjdk-8-jdk
之后开始编译
~/code/android11$ source build/envsetup.sh
~/code/android11$ lunch aosp_blueline-userdebug
~/code/android11$ make -j16
编译的过程时间也是非常的长,大约三个小时,不过第一次编译之后,下一次再进行编译就不用整体编译一遍了,只需要编译修改的源码部分。
为pixel3烧录aosp11
首先我们解锁OEM,开发者选项里面解开就行 之后bootloader解锁,手机连接上电脑后,打开命令行
adb reboot bootloader
fastboot deivces
fastboot flashing unlock
fastboot reboot
烧录,由于我使用的是linux服务器,本地机子是Windows,所以每次编译完成后给将编译好的系统进行打包下载到本地再烧录。 打包命令
tar -zcvf blueline.tar.gz blueline/
~/code/android11$ export ANDROID_PRODUCT_OUT=./out/target/product/blueline
set ANDROID_PRODUCT_OUT=这里是blueline的路径
~/code/android11$ fastboot flashall -w
aosp11 root过程
在源码里面总共修改11个文件
system/core/init/selinux.cpp
frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
build/make/target/product/base_system.mk
/system/core/libcutils/fs_config.cpp
/frameworks/base/core/jni/com_android_internal_os_Zygote.cpp
system/core/adb/Android.bp
system/core/adb/daemon/main.cpp
system/core/fs_mgr/Android.bp
system/sepolicy/Android.mk
systen/sepolicy/definitions.mk
frameworks/base/services/usb/java/com/android/server/usb/UsbDeviceManager.java
1、修改SELinux权限为Permissive
system/core/init/selinux.cpp
bool IsEnforcing() {
+ return false;
if (ALLOW_PERMISSIVE_SELINUX) {
return StatusFromCmdline() == SELINUX_ENFORCING;
}
return true;
}
frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
if (!Build.isBuildConsistent()) {
Slog.e(TAG, "Build fingerprint is not consistent, warning user");
mUiHandler.post(() -> {
if (mShowDialogs) {
AlertDialog d = new BaseErrorDialog(mUiContext);
d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
d.setCancelable(false);
d.setTitle(mUiContext.getText(R.string.android_system_label));
d.setMessage(mUiContext.getText(R.string.system_error_manufacturer));
d.setButton(DialogInterface.BUTTON_POSITIVE,
mUiContext.getText(R.string.ok),
mUiHandler.obtainMessage(DISMISS_DIALOG_UI_MSG, d));
- d.show();
}
});
}
}
}
2、增加su相关,确保apk root权限
需要编译su.cpp到system/bin目录下,为adb添加remount命令 build/make/target/product/base_system.mk
PRODUCT_PACKAGES += \
remout \
su \
注册用户组权限检测 system/extras/su/su.cpp
int main(int argc, char** argv) {
// uid_t current_uid = getuid();
// if (current_uid != AID_ROOT && current_uid != AID_SHELL) error(1, 0, "not allowed");
// Handle -h and --help.
++argv;
if (*argv && (strcmp(*argv, "--help") == 0 || strcmp(*argv, "-h") == 0)) {
fprintf(stderr,
"usage: su [WHO [COMMAND...]]\n"
"\n"
"Switch to WHO (default 'root') and run the given COMMAND (default sh).\n"
"\n"
"WHO is a comma-separated list of user, group, and supplementary groups\n"
"in that order.\n"
"\n");
return 0;
}
给su文件默认授予root权限 /system/core/libcutils/fs_config.cpp
// the following two files are INTENTIONALLY set-uid, but they
// are NOT included on user builds.
{ 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/procmem" },
{ 06755, AID_ROOT, AID_SHELL, 0, "system/bin/su" },
{ 06755, AID_ROOT, AID_SHELL, 0, "system/xbin/su" },
/frameworks/base/core/jni/com_android_internal_os_Zygote.cpp
static void DropCapabilitiesBoundingSet(fail_fn_t fail_fn) {
// for (int i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) {;
// if (prctl(PR_CAPBSET_DROP, i, 0, 0, 0) == -1) {
// if (errno == EINVAL) {
// ALOGE("prctl(PR_CAPBSET_DROP) failed with EINVAL. Please verify "
// "your kernel is compiled with file capabilities support");
// } else {
// fail_fn(CREATE_ERROR("prctl(PR_CAPBSET_DROP, %d) failed: %s", i, strerror(errno)));
// }
// }
// }
}
5、解锁fastboot,并关闭verity按需操作
system/core/adb/Android.bp
cc_defaults {
name: "adbd_defaults",
defaults: ["adb_defaults"],
//改了这里
- cflags: ["-UADB_HOST", "-DADB_HOST=0"],
+ //cflags: ["-UADB_HOST", "-DADB_HOST=0"],
+ cflags: [
+ "-UADB_HOST",
+ "-DADB_HOST=0",
+ "-UALLOW_ADBD_ROOT",
+ "-DALLOW_ADBD_ROOT=1",
+ "-DALLOW_ADBD_DISABLE_VERITY",
+ "-DALLOW_ADBD_NO_AUTH",
],
}
cc_library {
name: "libadbd_services",
defaults: ["adbd_defaults", "host_adbd_supported"],
recovery_available: true,
compile_multilib: "both",
....
//改了这里
+ required: [ "remount",],
target: {
android: {
srcs: [
"daemon/abb_service.cpp",
system/core/adb/daemon/main.cpp
static void drop_privileges(int server_port) {
ScopedMinijail jail(minijail_new());
// Add extra groups:
// AID_ADB to access the USB driver
// AID_LOG to read system logs (adb logcat)
// AID_INPUT to diagnose input issues (getevent)
// AID_INET to diagnose network issues (ping)
// AID_NET_BT and AID_NET_BT_ADMIN to diagnose bluetooth (hcidump)
// AID_SDCARD_R to allow reading from the SD card
// AID_SDCARD_RW to allow writing to the SD card
// AID_NET_BW_STATS to read out qtaguid statistics
// AID_READPROC for reading /proc entries across UID boundaries
// AID_UHID for using 'hid' command to read/write to /dev/uhid
gid_t groups[] = {AID_ADB, AID_LOG, AID_INPUT, AID_INET,
AID_NET_BT, AID_NET_BT_ADMIN, AID_SDCARD_R, AID_SDCARD_RW,
AID_NET_BW_STATS, AID_READPROC, AID_UHID};
minijail_set_supplementary_gids(jail.get(), arraysize(groups), groups);
// Don't listen on a port (default 5037) if running in secure mode.
// Don't run as root if running in secure mode.
if (should_drop_privileges()) {
//改了这里
- //const bool should_drop_caps = !__android_log_is_debuggable();
+ const bool should_drop_caps = false;
if (should_drop_caps) {
minijail_use_caps(jail.get(), CAP_TO_MASK(CAP_SETUID) | CAP_TO_MASK(CAP_SETGID));
}
system/core/fs_mgr/Android.bp
whole_static_libs: [
"liblogwrap",
"libdm",
"libext2_uuid",
"libfscrypt",
"libfstab",
],
cppflags: [
- "-DALLOW_ADBD_DISABLE_VERITY=0"
+ "-UALLOW_ADBD_DISABLE_VERITY",
+ "-DALLOW_ADBD_DISABLE_VERITY=1",
],
product_variables: {
debuggable: {
cppflags: [
"-UALLOW_ADBD_DISABLE_VERITY",
"-DALLOW_ADBD_DISABLE_VERITY=1",
],
},
},
srcs: [
"fs_mgr_remount.cpp",
],
cppflags: [
- "-DALLOW_ADBD_DISABLE_VERITY=0",
+ "-UALLOW_ADBD_DISABLE_VERITY",
+ "-DALLOW_ADBD_DISABLE_VERITY=1",
],
product_variables: {
debuggable: {
cppflags: [
"-UALLOW_ADBD_DISABLE_VERITY",
"-DALLOW_ADBD_DISABLE_VERITY=1",
],
},
},
user版本启动overlayfs来装载remount对应分区 system/sepolicy/Android.mk
ifneq ($(filter address,$(SANITIZE_TARGET)),)
local_fc_files += $(wildcard $(addsuffix /file_contexts_asan, $(PLAT_PRIVATE_POLICY)))
endif
-ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
+ifneq (,$(filter user userdebug eng,$(TARGET_BUILD_VARIANT)))
local_fc_files += $(wildcard $(addsuffix /file_contexts_overlayfs, $(PLAT_PRIVATE_POLICY)))
endif
ifeq ($(TARGET_FLATTEN_APEX),true)
file_contexts.device.tmp :=
file_contexts.local.tmp :=
systen/sepolicy/definitions.mk
define transform-policy-to-conf
@mkdir -p $(dir $@)
$(hide) $(M4) --fatal-warnings $(PRIVATE_ADDITIONAL_M4DEFS) \
-D mls_num_sens=$(PRIVATE_MLS_SENS) -D mls_num_cats=$(PRIVATE_MLS_CATS) \
- -D target_build_variant=$(PRIVATE_TARGET_BUILD_VARIANT)
+ -D target_build_variant=eng \
-D target_with_dexpreopt=$(WITH_DEXPREOPT) \
-D target_arch=$(PRIVATE_TGT_ARCH) \
-D target_with_asan=$(PRIVATE_TGT_WITH_ASAN) \
-D target_with_native_coverage=$(PRIVATE_TGT_WITH_NATIVE_COVERAGE) \
-D target_full_treble=$(PRIVATE_SEPOLICY_SPLIT) \
-D target_compatible_property=$(PRIVATE_COMPATIBLE_PROPERTY) \
-D target_treble_sysprop_neverallow=$(PRIVATE_TREBLE_SYSPROP_NEVERALLOW) \
-D target_exclude_build_test=$(PRIVATE_EXCLUDE_BUILD_TEST) \
-D target_requires_insecure_execmem_for_swiftshader=$(PRODUCT_REQUIRES_INSECURE_EXECMEM_FOR_SWIFTSHADER) \
$(PRIVATE_TGT_RECOVERY) \
-s $(PRIVATE_POLICY_FILES) > $@
endef
.KATI_READONLY := transform-policy-to-conf
6、默认开启OEM和去除OEM解锁警告提示
默认开启OEM解锁选项 frameworks/base/services/usb/java/com/android/server/usb/UsbDeviceManager.java
protected void finishBoot() {
+ android.service.oemlock.OemLockManager mOemLockManager
+ = (android.service.oemlock.OemLockManager) mContext.getSystemService(Context.OEM_LOCK_SERVICE);
+ mOemLockManager.setOemUnlockAllowedByUser(true);
+
if (mBootCompleted && mCurrentUsbFunctionsReceived && mSystemReady) {
if (mPendingBootBroadcast) {
updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions));
su代码修改,实现root权限管理APK,功能包括APP申请root权限管理,root权限请求记录。
首先我们来分析一下app应用如何执行root权限的命令,可以看出,应用先要执行su命令,然后再执行想要执行的命令,那我们在管理应用root权限的时候,首先我们需要知道是哪个应用需要执行root命令,还需要获取本机所有的应用,并控制这些应用app执行root命令的权限。
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class RootCommand {
public static String runCommand(String command){
Process process = null;
String result = "";
DataOutputStream os = null;
DataInputStream is = null;
try{
process = Runtime.getRuntime().exec("su");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
os = new DataOutputStream((process.getOutputStream()));
is = new DataInputStream(process.getInputStream());
os.writeBytes(command + "\n");
os.writeBytes("exit\n");
os.flush();
String line = null;
while((line = is.readLine())!= null){
result+=line;
result += "\n";
}
process.waitFor();
}catch (IOException | InterruptedException e){
e.printStackTrace();
}
finally {
if (os != null){
try{
os.close();
}catch (IOException e){
e.printStackTrace();
}
}
if (is != null){
try{
is.close();
}catch (IOException e){
e.printStackTrace();
}
}
if (process != null){
process.destroy();
}
}
return result;
}
}
root权限管理思路 1.我们让应用在执行su命令的时候获取这个应用的包名,具体实现方式是:在su源代码中实现localsocket连接,和应用层的supersu进行通信,然后通过getppid()获取调用su命令的应用的pid,之后再通过cat /proc/"+ pid +"/cmdline -r 命令获取进程名称(应用包名),之后将包名通过localsocket发送给supersu,在supersu里接收到包名后,检测此应用是否获得了root权限授权,决定是否切换为root权限。 2.利用List allApps = getPackageManager().getInstalledApplications(0);获取本机安装的所有app应用信息,其中包括包名,然后使用sqlite数据库将所有包名和是否获得root授权的信息建表,对权限进行管理。
su.cpp修改
#define PATH "supersu.localsocket"
int main(int argc, char **argv)
{
int socketID, ret;
char sendStr[10];
pid_t fpid = getppid();
snprintf(sendStr, 10, "%d", (int)fpid);
int count = 0;
for (int i = 0; i < 10; i++)
{
if (sendStr[i] != 0)
{
count = count*10 + (sendStr[i] - '0');
}else{
break;
}
}
std::stringstream ss;
std::string str;
ss<<count;
ss>>str;
FILE *fp = NULL;
char data[100] = {'0'};
std::string cmd = "cat /proc/"+ str +"/cmdline";
char buf[40];
strcpy(buf, cmd.c_str());
fp = popen(buf, "r");
if (fp == NULL)
{
printf("popen error!\n");
return 1;
}
while (fgets(data, sizeof(data), fp) != NULL)
{
printf("shell result is : %s\n", data);
}
pclose(fp);
socketID = socket_local_client(PATH, 0, SOCK_STREAM);
if (socketID < 0)
{
return socketID;
}
ret = write(socketID, data, strlen(data));
if (ret < 0)
{
return ret;
}
char recvStr[10];
ret = read(socketID, recvStr, sizeof(recvStr));
if (ret < 0)
{
return ret;
}
if (recvStr[0] == '1')
{
++argv;
if (*argv && (strcmp(*argv, "--help") == 0 || strcmp(*argv, "-h") == 0))
{
fprintf(stderr,
"usage: su [WHO [COMMAND...]]\n"
"\n"
"Switch to WHO (default 'root') and run the given COMMAND (default sh).\n"
"\n"
"WHO is a comma-separated list of user, group, and supplementary groups\n"
"in that order.\n"
"\n");
return 0;
}
uid_t uid = 0;
gid_t gid = 0;
if (*argv)
{
gid_t gids[10];
int gids_count = sizeof(gids) / sizeof(gids[0]);
extract_uidgids(*argv, &uid, &gid, gids, &gids_count);
if (gids_count)
{
if (setgroups(gids_count, gids))
{
error(1, errno, "setgroups failed");
}
}
++argv;
}
if (setgid(gid))
error(1, errno, "setgid failed");
if (setuid(uid))
error(1, errno, "setuid failed");
setenv("PATH", _PATH_DEFPATH, 1);
unsetenv("IFS");
struct passwd *pw = getpwuid(uid);
if (pw)
{
setenv("LOGNAME", pw->pw_name, 1);
setenv("USER", pw->pw_name, 1);
}
else
{
unsetenv("LOGNAME");
unsetenv("USER");
}
char *exec_args[argc + 1];
size_t i = 0;
for (; *argv != NULL; ++i)
{
exec_args[i] = *argv++;
}
if (i == 0)
exec_args[i++] = const_cast<char *>("/system/bin/sh");
exec_args[i] = NULL;
execvp(exec_args[0], exec_args);
error(1, errno, "failed to exec %s", exec_args[0]);
return 0;
}
}
supersu关键代码: 1.应用初始化创建记录应用授权记录的数据库
dbHelper = new MyDatabaseHelper(this, "/data/data/edu.scse.supersu/databases/rootManager.db", 1);
List<ApplicationInfo> allApps = getPackageManager().getInstalledApplications(0);
for (ApplicationInfo ai : allApps) {
Cursor cursor = dbHelper.getReadableDatabase().rawQuery("select * from AppRoot where appPackageName = '" + ai.packageName + "'", null);
if (cursor.getCount() == 0){
if (ai.packageName.equals("edu.scse.supersu")){
dbHelper.getWritableDatabase().execSQL("insert into AppRoot values(null,?,1)", new String[]{ai.packageName});
}else{
dbHelper.getWritableDatabase().execSQL("insert into AppRoot values(null,?,0)", new String[]{ai.packageName});
}
}
}
2. (1)注册服务,监听来自su的localsocket的连接并进行通信授权,如果执行root命令时没有被授予root权限,则弹出授权框,询问用户是否进行授权。
RootManagementService.java
package edu.scse.supersu.service;
import android.Manifest;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.Service;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.net.Credentials;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
import android.net.Uri;
import android.os.Build;
import android.os.IBinder;
import android.os.Looper;
import android.provider.Settings;
import android.util.Log;
import android.view.WindowManager;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.List;
import java.util.Objects;
import edu.scse.supersu.Sql.MyDatabaseHelper;
import edu.scse.supersu.common.RootCommand;
public class RootManagementService extends Service {
private static final String localSocketName = "supersu.localsocket";
private ServerThread mThread = null;
private MyDatabaseHelper dbHelper;
public RootManagementService() {
}
@Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onCreate() {
super.onCreate();
dbHelper = new MyDatabaseHelper(this, "/data/data/edu.scse.supersu/databases/rootManager.db", 1);
System.out.println("service is created\n");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("service 启动\n");
startServer();
return START_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
System.out.println("serivce 退出\n");
}
private void startServer() {
stopServer();
mThread = new ServerThread();
mThread.start();
}
private void stopServer() {
if (mThread != null) {
mThread.exit();
mThread = null;
}
}
private class ServerThread extends Thread {
private boolean exit = false;
public void run() {
LocalServerSocket server = null;
try {
server = new LocalServerSocket(localSocketName);
while (!exit) {
LocalSocket connect = server.accept();
Credentials cre = connect.getPeerCredentials();
Log.i("RootManagement", "accept socket uid:" + cre.getUid());
new ConnectThread(connect).start();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void exit() {
exit = true;
}
}
class ConnectThread extends Thread {
LocalSocket socket = null;
BufferedReader mBufferedReader = null;
InputStream input = null;
PrintWriter send = null;
String readString = null;
public ConnectThread(LocalSocket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
input = socket.getInputStream();
byte[] buffer = new byte[100];
int len = input.read(buffer);
send = new PrintWriter(socket.getOutputStream());
String packageName = new String(buffer, "utf-8");
packageName = packageName.substring(0, len);
Log.d("RootManagement", "packageName is :" + packageName);
Cursor cursor = dbHelper.getReadableDatabase().rawQuery("select * from AppRoot where appPackageName = '" + packageName + "'", null);
if (cursor.getCount() == 0) {
send.println("B\0");
}
cursor.moveToNext();
String isRoot = cursor.getString(cursor.getColumnIndex("isRoot"));
if (isRoot.charAt(0) == '1') {
send.println("1\0");
} else {
Looper.prepare();
AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext())
.setIcon(android.R.drawable.ic_dialog_info)
.setTitle("root授权通知")
.setMessage("是否要授予此应用root权限?")
.setPositiveButton("授予",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int whichButton) {
send.println("A\0");
send.flush();
send.close();
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
builder.setNegativeButton("拒绝", null);
AlertDialog dialog = builder.create();
dialog.setCancelable(false);
dialog.setCanceledOnTouchOutside(false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Objects.requireNonNull(dialog.getWindow()).setType((WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY));
} else {
Objects.requireNonNull(dialog.getWindow()).setType((WindowManager.LayoutParams.TYPE_SYSTEM_ALERT));
}
dialog.show();
Looper.loop();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
send.flush();
send.close();
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
(2)root权限管理页面
activity_root_manager.xml 应用信息列表
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".RootManager">
<androidx.appcompat.widget.SearchView
android:id="@+id/search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:iconifiedByDefault="false"></androidx.appcompat.widget.SearchView>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/refresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/RootManage"
android:layout_width="match_parent"
android:layout_height="wrap_content"></androidx.recyclerview.widget.RecyclerView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</LinearLayout>
array_item.xml 列表子项
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:descendantFocusability="beforeDescendants"
android:minHeight="80dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center|start"
android:orientation="horizontal"
android:padding="10dp">
<ImageView
android:id="@+id/appIcon"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginRight="10dp"
android:scaleType="fitXY" />
<LinearLayout
android:layout_width="218dp"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center|start"
android:orientation="horizontal">
<TextView
android:id="@+id/appName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start"
android:textColor="#000"
android:textSize="18sp"
android:textStyle="bold" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:gravity="center|start"
android:orientation="horizontal">
<TextView
android:id="@+id/packageName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:padding="3dp"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
<Button
android:id="@+id/grant"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="授予"
android:textSize="19dp">
</Button>
</LinearLayout>
</LinearLayout>
RootManager.java root权限管理
package edu.scse.supersu;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.SearchView;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.widget.CursorAdapter;
import android.widget.LinearLayout;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import edu.scse.supersu.Sql.MyDatabaseHelper;
import edu.scse.supersu.adapter.RootAdapter;
public class RootManager extends AppCompatActivity {
RecyclerView rootManagerList;
List<Map<String, Object>> appList = new ArrayList<>();
SearchView searchView;
SwipeRefreshLayout swipeRefreshLayout;
MyDatabaseHelper dbHelper;
RootAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_root_manager);
rootManagerList = findViewById(R.id.RootManage);
swipeRefreshLayout = findViewById(R.id.refresh);
searchView = findViewById(R.id.search);
rootManagerList.setHasFixedSize(true);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
rootManagerList.setLayoutManager(linearLayoutManager);
adapter = new RootAdapter(this, appList);
rootManagerList.setAdapter(adapter);
getAllAppInfo();
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
refreshList();
}
});
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
searchAppInfo(query);
return true;
}
@Override
public boolean onQueryTextChange(String newText) {
if (newText.equals("")) {
getAllAppInfo();
}
return true;
}
});
}
private void searchAppInfo(String query) {
List<Map<String, Object>> tempList = new ArrayList<>();
appList.clear();
dbHelper = new MyDatabaseHelper(this, "/data/data/edu.scse.supersu/databases/rootManager.db", 1);
Cursor cursor = dbHelper.getReadableDatabase().rawQuery("select * from AppRoot where appPackageName LIKE '%" + query + "%'", null);
if (cursor.getCount() != 0) {
while (cursor.moveToNext()) {
Map<String, Object> item = new HashMap<String, Object>();
String packageName = cursor.getString(cursor.getColumnIndex("appPackageName"));
String isRoot = cursor.getString(cursor.getColumnIndex("isRoot"));
try {
Drawable appIcon = getPackageManager().getApplicationIcon(packageName);
item.put("appIcon", appIcon);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
try {
ApplicationInfo applicationInfo = getPackageManager().getApplicationInfo(packageName, 0);
String name = (String) getPackageManager().getApplicationLabel(applicationInfo);
item.put("appName", name);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
item.put("appPackageName", packageName);
item.put("isRoot", isRoot);
tempList.add(item);
}
}
appList.addAll(tempList);
adapter.notifyDataSetChanged();
}
private void getAllAppInfo() {
List<Map<String, Object>> tempList = new ArrayList<>();
appList.clear();
List<ApplicationInfo> allApps = getPackageManager().getInstalledApplications(0);
for (ApplicationInfo info : allApps) {
Map<String, Object> item = new HashMap<String, Object>();
String name = (String) getPackageManager().getApplicationLabel(info);
item.put("appName", name);
try {
Drawable appIcon = getPackageManager().getApplicationIcon(info.packageName);
item.put("appIcon", appIcon);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
item.put("appPackageName", info.packageName);
dbHelper = new MyDatabaseHelper(this, "/data/data/edu.scse.supersu/databases/rootManager.db", 1);
Cursor cursor = dbHelper.getReadableDatabase().rawQuery("select * from AppRoot where appPackageName = '" + info.packageName + "'", null);
if (cursor.getCount() != 0) {
cursor.moveToNext();
String isRoot = cursor.getString(cursor.getColumnIndex("isRoot"));
item.put("isRoot", isRoot);
}
tempList.add(item);
}
appList.addAll(tempList);
adapter.notifyDataSetChanged();
}
private void refreshList() {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
runOnUiThread(new Runnable() {
@Override
public void run() {
getAllAppInfo();
adapter.notifyDataSetChanged();
swipeRefreshLayout.setRefreshing(false);
}
});
}
}).start();
}
private void searchRefreshList() {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
runOnUiThread(new Runnable() {
@Override
public void run() {
adapter.notifyDataSetChanged();
swipeRefreshLayout.setRefreshing(false);
}
});
}
}).start();
}
}
一键开启USB调试和WIFI一键开关
思路:应用获取root权限之后,就可以通过执行adb命令来打开usb调试和wifi
实现USB调试功能一键开关,WiFi一键开关
openUSB.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String result = RootCommand.runCommand("settings put global adb_enabled 1");
System.out.println(result);
}
});
closeUSB.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String result = RootCommand.runCommand("settings put global adb_enabled 0");
System.out.println(result);
}
});
openWiFi.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String result = RootCommand.runCommand("svc wifi enable");
System.out.println(result);
}
});
closeWiFi.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String result = RootCommand.runCommand("svc wifi disable");
System.out.println(result);
}
});
实现AOSP系统内置制定WiFi名称与密码,刷机后可自动链接制定WiFi
需要修改的文件
out/target/product/generic/vendor/etc/wifi/wpa_supplicant.conf
external/wpa_supplicant_8/wpa_supplicant/wpa_supplicant.conf
修改wpa_supplicant.conf
network={
ssid="WiFi名称"
psk="WiFi密码"
key_mgmt=WPA-PSK
}
为AOSP11版本系统添加OpenSSH
Android11源代码中已经有了openssh包,位于external/openssh;Android系统编译的时候默认没有添加openssh,所以需要在Android.mk中配置openssh编译。
openssh包含以下模块: scp, sftp, ssh, sshd, sshd_config, ssh-keygen, start-ssh
在device/google/crosshatch/aosp_blueline.mk中进行以下修改
PRODUCT_PACKAGES += \
com.android.vndk.current.on_vendor \
scp \
sftp \
ssh \
sshd \
sshd_config \
ssh-keygen \
start-ssh \
wpa_supplicant
系统编译完之后,能够看到如下文件
/system/bin/ssh
/system/bin/ssh-keygen
/system/bin/sshd
/system/bin/start-ssh
/system/bin/scp
/system/bin/sftp
/system/etc/ssh/sshd_config
配置ssh (1)创建目录结构
mkdir -p /data/ssh/empty
chmod 700 /data/ssh
chmod 700 /data/ssh/empty
(2)生成配置文件
cat /system/etc/ssh/sshd_config | \
sed 's/#PermitRootLogin yes$/PermitRootLogin without-password/' | \
sed 's/#RSAAuthentication yes/RSAAuthentication yes/' | \
sed 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/' | \
sed 's/PasswordAuthentication no/#PasswordAuthentication no/' | \
sed 's/#PermitEmptyPasswords no/PermitEmptyPasswords yes/' | \
sed 's/#ChallengeResponseAuthentication yes/ChallengeResponseAuthentication yes/' | \
sed 's/#UsePrivilegeSeparation yes/UsePrivilegeSeparation no/' | \
sed 's;/usr/libexec/sftp-server;internal-sftp;' > \
/data/ssh/sshd_config
chmod 600 /data/ssh/sshd_config
(3)生成密钥 在Windows/Linux上通过下面的命令来生成密钥
ssh-keygen -t rsa -C "your_email_address"
上面的命令会在主目录下生成.ssh目录, 目录包含id_rsa(私钥)和id_rsa.pub(公钥)两个文件,然后通过adb等命令将id_rsa.pub上传至Android中
adb push id_rsa.pub /data/ssh/authorized_keys
chmod 600 /data/ssh/authorized_keys
chown root:root /data/ssh/authorized_keys
(4)生成启动脚本
mkdir -p /data/local/userinit.d
cat /system/bin/start-ssh | \
sed 's;/system/etc/ssh/sshd_config;/data/ssh/sshd_config;' > \
/data/local/userinit.d/99sshd
chmod 755 /data/local/userinit.d/99sshd
通过上面的命令单独生成一个启动脚本 然后就可以通过执行下面的脚本来启动sshd
/data/local/userinit.d/99sshd
(5)连接sshd 使用命令即可连接sshd
ssh root@ip
|