导图社区 知识库
个人知识技巧:包含了方案配置、性能分析、应用、常用命令及原理;常用函数等内容,需要的可以收藏下哟。
编辑于2022-03-24 13:32:08知识库
Android
方案配置
PRODUCT_DEXPREOPT_SPEED_APPS
New in Android O一个应用列表,其中的应用被确定为产品的核心应用,并且应使用 speed 编译过滤器进行编译。例如,常驻应用(如 SystemUI)只有在下次系统重新启动时才有机会使用配置文件引导型编译,因此可能最好是让产品始终对这些应用进行 AOT 编译。
PRODUCT_SYSTEM_SERVER_APPS
New in Android O系统服务器加载的应用的列表。这些应用将默认使用 speed 编译过滤器进行编译。
新增GLOBAL_REMOVED_PACKAGES
作用:删除一些默认配置的PRODUCT_PACKAGES
原理;build系统会将一些OVERRIDE的packages过滤掉,利用这个机制增加对GLOBAL_REMOVED_PACKAGES的支持
实现
使用BUILD_PACAKGE的LOCAL_OVERRIDES_PACKAGES
新增一个BUILD_PACKAGE的模块,实现一个空的应用,利用其LOCAL_OVERRIDES_PACKAGES字段进行实现。缺点:out下会多出一个无用的空应用。优点:不用修改build下的原生脚本,且几乎不会随版本升级而发生变化。###############################################################################LOCAL_PATH:= $(call my-dir)include $(CLEAR_VARS)LOCAL_PACKAGE_NAME := PackageOverrideLOCAL_MODULE_TAGS := optionalLOCAL_CERTIFICATE := platformLOCAL_PRODUCT_MODULE := trueLOCAL_OVERRIDES_PACKAGES := $(GLOBAL_REMOVED_PACKAGES)#LOCAL_SRC_FILES := $(call all-java-files-under, src)LOCAL_SDK_VERSION := currentinclude $(BUILD_PACKAGE)
修改编译脚本
AndroidP
diff --git a/core/main.mk b/core/main.mkindex 9beb3e64c..d8a2e8ec8 100644--- a/core/main.mk+++ b/core/main.mk@@ -907,6 +907,9 @@ endif product_MODULES := $(filter-out $(foreach m, $(product_MODULES), \ $(EXECUTABLES.$(m).OVERRIDES)), $(product_MODULES))+ product_MODULES := $(filter-out $(foreach m, $(product_MODULES), \+ $(GLOBAL_REMOVED_PACKAGES)), $(product_MODULES))+ # Resolve the :32 :64 module name modules_32 := $(patsubst %:32,%,$(filter %:32, $(product_MODULES))) modules_64 := $(patsubst %:64,%,$(filter %:64, $(product_MODULES)))
AndroidR
diff --git a/core/main.mk b/core/main.mkindex 357c70da3..7d9e0ab1f 100644--- a/core/main.mk+++ b/core/main.mk@@ -1064,6 +1064,7 @@ define product-installed-files ) \ $(eval ### Filter out the overridden packages and executables before doing expansion) \ $(eval _pif_overrides := $(call module-overrides,$(_pif_modules))) \+ $(eval _pif_overrides += $(GLOBAL_REMOVED_PACKAGES)) \ $(eval _pif_modules := $(filter-out $(_pif_overrides), $(_pif_modules))) \ $(eval ### Resolve the :32 :64 module name) \ $(eval _pif_modules_32 := $(patsubst %:32,%,$(filter %:32, $(_pif_modules)))) \
Android.mk起别名
LOCAL_MODULE_STEM
性能分析
bootchart
https://source.android.com/devices/tech/perf/boot-times#bootchart
system/core/init/grab-bootchart.sh(官方,可能缺一些命令)
获取bootchart数据
touch /data/bootchart/enabled
cd /data/bootchart && tar -czf bootchart.tgz header proc_diskstats.log proc_ps.log proc_stat.log
生成png
java -jar bootchart.jar bootchart.tgz
bootchart.jar
下载:https://sourceforge.net/projects/bootchart/files/bootchart/0.9/
ant编译
systrace
https://source.android.com/devices/tech/perf/boot-times#systrace
代码修改
1、frameworks/native/atrace/atrace.rc 将write /sys/kernel/(debug/)tracing/tracing_on 0注释,默认是关闭trace,需要将其打开2、device.mk PRODUCT_PROPERTY_OVERRIDES += debug.atrace.tags.enableflags=802922 PRODUCT_PROPERTY_OVERRIDES += persist.traced.enable=03、BoardConfig.mk BOARD_KERNEL_CMDLINE := ... trace_buf_size=64M trace_event=sched_wakeup,sched_switch,sched_blocked_reason,sched_cpu_hotplug4、在设备专用的 init.rc 文件中 on property:sys.boot_completed=1(这会在启动完成后停止跟踪) write /d/tracing/tracing_on 0 write /d/tracing/events/ext4/enable 0 write /d/tracing/events/f2fs/enable 0 write /d/tracing/events/block/enable 0
抓取和生成html
手动抓取
atrace -c -b 40960 --async_start -z gfx video camera hal ss --- 开始抓traceatrace -c -b 40960 --async_stop -z > /sdcard/camera.trace --- 结束抓trace抓完后pull出来,到android的external/chromium-trace目录下执行 ./systrace.py --from-file=xxxxx
内存信息
vmstat
命令:vmstat -- usage: vmstat [-n] [DELAY [COUNT]]procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu---- r b swpd free buff cache si so bi bo in cs us sy id wa 1 0 0 385044 12532 646516 0 0 10 2 0 1082 3 6 91 0 1 0 0 385416 12532 646516 0 0 0 0 0 3925 3 4 93 0 0 0 0 384736 12532 646524 0 0 0 20 0 3844 3 5 92 0 0 0 0 384704 12532 646524 0 0 0 0 0 3402 1 3 96 0 0 0 0 384996 12532 646524 0 0 0 0 0 3511 1 3 96 0 0 0 0 384592 12532 646528 0 0 0 0 0 3542 1 3 96 0proc组,与进程相关的数据: r:当前运行和等待CPU时间片的进程数(就绪状态),如果该值长期大于CPU个数,表明CPU性能不足。 b:表示在等待资源的进程数,比如正在等待I/O、或者内存交换等。memory组,与内存相关的数据: swpd:表示切换到内存交换区的内存数量(以k为单位)。如果swpd的值不为0,或者比较大,只要si、so的值长期为0,这种情况下一般不用担心,不会影响系统性能。 free:表示当前空闲的物理内存数量(以k为单位) buff:表示buffers cache的内存数量,一般对块设备的读写才需要缓冲。 cache:表示page cached的内存数量,一般作为文件系统cached,频繁访问的文件都会被cached,如果cache值较大,说明cached的文件数较多,如果此时IO中bi比较小,说明文件系统效率比较好。swap组,交换区的数据 si:表示由磁盘调入内存,也就是内存进入内存交换区的数量。 so:表示由内存调入磁盘,也就是内存交换区进入内存的数量。 一般情况下,si、so的值都为0,如果si、so的值长期不为0,则表示系统内存不足。需要增加系统内存。前面swpd的值如果非0,但si,so的值长期为0,表示有一部分长期不使用的内存数据交换到交换区了,这种情况当前不会响应系统性能。io组,显示磁盘读写状况 bi:表示从块设备读入数据的总量(即读磁盘)(每秒kb) bo:表示写入到块设备的数据总量(即写磁盘)(每秒kb) 这里我们设置的bi+bo参考值为1000,如果超过1000,而且wa值较大,则表示系统磁盘IO有问题,应该考虑提高磁盘的读写性能。system 显示采集间隔内发生的中断数 in:表示在某一时间间隔中观测到的每秒设备中断数。 cs:表示每秒产生的上下文切换次数。 上面这2个值越大,会看到由内核消耗的CPU时间会越多。CPU项显示了CPU的使用状态 us:显示了用户进程消耗的CPU 时间百分比。us的值比较高时,说明用户进程消耗的cpu时间多,但是如果长期大于50%,就需要考虑优化程序或算法。 sy:显示了内核进程消耗的CPU时间百分比。Sy的值较高时,说明内核消耗的CPU资源很多。 根据经验,us+sy的参考值为80%,如果us+sy大于 80%说明可能存在CPU资源不足。 id:显示了CPU处在空闲状态的时间百分比。 wa:示了IO等待所占用的CPU时间百分比。wa值越高,说明IO等待越严重,根据经验,wa的参考值为20%,如果wa超过20%,说明IO等待严重,引起IO等待的原因可能是磁盘大量随机读写造成的,也可能是磁盘或者磁盘控制器的带宽瓶颈造成的(主要是块操作)。
应用
反编译
本地反编译工具
Onekey-Decompile-Apk
在线反编译工具
http://www.javadecompilers.com/
oat/odex的反编译
工具
baksmali-2.5.2.jar
smali-2.5.2.jar
jad
javap
命令
odex -> smali
java -jar baksmali-2.5.2.jar deodex services.odex -o service
smali -> dex
java -jar smali-2.5.2.jar a service -o service.dex
dex -> jar
按Onekey-Decompile-Apk或在线编译弄
class的反编译
javap -c,字节码层次的反编译
子主题
手动抓取hprof文件
am dumpheap com.allwinner.batterytest PATH
打印应用签名信息
keytool -list -printcert -jarfile xxxx.apk
adb在线调试
PC端
adb tcpip 5555(设置端口号)
adb connect deviceIp::5555
设备端
setprop service.adb.tcp.port 5555
restart adbd
frameworks
修改java的final static变量值
例如修改Build.java中的FINGERPRINT的值,由于所有应用都是由zygote fork出来的,因此这个值从系统起来后就固定了,但某些场景(应用)下,我们又希望修改这个值。可通过以下的修改:+ try {+ Field modelField = Build.class.getDeclaredField("FINGERPRINT");+ modelField.setAccessible(true);+ Field accessFlags = modelField.getClass().getDeclaredField("accessFlags");+ accessFlags.setAccessible(true);+ accessFlags.setInt(modelField, modelField.getModifiers() & ~Modifier.FINAL);+ modelField.set(null, "google/walleye/walleye:10/QP1A.191105.004/eng.chongy.20201217.090229:userdebug/test-keys");+ accessFlags.setInt(modelField, modelField.getModifiers() | Modifier.FINAL);+ } catch (IllegalAccessException | NoSuchFieldException e) {+ Log.e("Zygote", "reflect failed", e);+ }大概意思是先通过放射拿到Field的accessFlags,然后删除FINAL的属性,这样才能修改其值,然后再将FINAL的属性设置回去。
强制安装应用是给予权限
安装应用后,PMS会为安装的应用赋予允许权限,然而部分权限涉及到安装位置(system app, platform app, priv -app)以及签名信息等问题而无法赋予权限。例如外装的GMS应用,如使用suplay安装的,由于是外装应用,非system应用或者platform应用,部分权限是没有的,导致其运行时会报错闪退,为了支持这种做法,在AndroidQ上,安装应用后会走到PermissionManagerService的restorePermissionState来根据应用所申请的权限进行赋予:1011 final String perm = bp.getName();1012 boolean allowedSig = false;1013 int grant = GRANT_DENIED;10141015 // Keep track of app op permissions.1016 if (bp.isAppOp()) {1017 mSettings.addAppOpPackage(perm, pkg.packageName);1018 }10191020 if (bp.isNormal()) {1021 // For all apps normal permissions are install time ones.1022 grant = GRANT_INSTALL;1023 } else if (bp.isRuntime()) {1024 if (origPermissions.hasInstallPermission(bp.getName())1025 || upgradedActivityRecognitionPermission != null) {1026 // Before Q we represented some runtime permissions as install permissions,1027 // in Q we cannot do this anymore. Hence upgrade them all.1028 grant = GRANT_UPGRADE;1029 } else {1030 // For modern apps keep runtime permissions unchanged.1031 grant = GRANT_RUNTIME;1032 }1033 } else if (bp.isSignature()) {1034 // For all apps signature permissions are install time ones.1035 allowedSig = grantSignaturePermission(perm, pkg, bp, origPermissions);1036 if (allowedSig) {1037 grant = GRANT_INSTALL;1038 }1039 } grant表示该权限在何时允许赋予,只要让grant为GRANT_INSTALL,那么应用则可在安装时就能获取到该权限。
ConfigurationChanged
大概位置:ActivityRecord.java ensureActivityConfiguration
所有应用强制横屏打开
1.修改PackageParser的parseActivity方法,每次启动都会解析Activity的信息,在这边,将所有的Activity都设置为横屏: if(SystemProperties.getBoolean("ro.product.app.force_landscape", false)) { /*if (a.info.toString().contains("com.XX.Activity")*/ a.info.screenOrientation = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; }2.修改DisplayContent的getOrientation方法,这个方法是在应用发出旋转或者有动画/命令/自动旋转等场景下来获取最终的方向,将其结果强制设置为横屏: if (SystemProperties.getBoolean("ro.product.app.force_landscape", false)){ orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; }
查看systemui的一些状态信息
dumpsys activity service com.android.systemui/.SystemUIService
Log.isLoggable的打开
setprop log.tag.XXXX (D/V/E/I/W)
设置属性后重启应用
settings常用设置
设置超时时间
settings put system screen_off_timeout 1800000
dumpsys信息
锁屏状态
dumpsys activity activities,查看KeyguardController的信息
在源码中调用dump
import java.io.StringWriter;import java.io.PrintWriter;StringWriter out = new StringWriter();PrintWriter pw = new PrintWriter(out);xxx.dump(pw, Prefix);Slog.d(TAG, out.toString());
启动systemUI
am start-service -n com.android.systemui/com.android.systemui.SystemUIService
wm size后动画修正
默认启动display的size和density
ro.config.size_override=1280,800
ro.config.density_override=213
主屏设置override size时设置属性
// frameworks/base/services/core/java/com/android/server/wm/DisplayWindowSettings.javavoid setForcedSize(DisplayContent displayContent, int width, int height) { if (displayContent.isDefaultDisplay) { final String sizeString = (width == 0 || height == 0) ? "" : (width + "," + height); Settings.Global.putString(mService.mContext.getContentResolver(), Settings.Global.DISPLAY_SIZE_FORCED, sizeString); SystemProperties.set("persist.display.size_force", sizeString); <-------------新增属性设置,用于SF和BootAnimation的获取。 return; }}
BootAnimation的大小修正
// frameworks/base/cmd/bootanimation/BootAnimation.cppstatus_t BootAnimation::readyToRun() { // 新增获取设置后的大小和默认设置的大小 int override_width = 0, override_height = 0; char buf[PROPERTY_VALUE_MAX]; char *val; buf[0] = '\0'; property_get("persist.display.size_force", buf, ""); if (buf[0] == '\0') { property_get("ro.config.size_override", buf, ""); } val = strtok(buf, ","); if (val != NULL) { override_width = atoi(val); } val = strtok(NULL, ","); if (val != NULL) { override_height = atoi(val); } if (override_width != 0 && override_height != 0) { switch (user_rotation) { case 90: case 270: width = override_height; height = override_width; break; default: width = override_width; height = override_height; break; } } else { …… }
修正SurfaceFlinger启动时DisplayDevice的frame和viewport大小
// frameworks/native/services/surfaceflinger/DisplayDevice.cpp// 新增方法,根据java层的LogicalDisplay对于wm size不规整的分辨率进行修正。// width/height为显示屏的真实的分辨率,overrideWidth/overrideHeight为override后的分辨率void DisplayDevice::getOverrideFrame(int width, int height, int overrideWidth, int overrideHeight, Rect & frame) { if (overrideWidth == 0 || overrideHeight == 0) { frame.set(Rect(width, height)); return; } // mDefaultRotation是我们自己加的代码,用于设置默认启动的方向。 int rotation = (mDisplayInstallOrientation + mDefaultRotation / 90) % DisplayState::eOrientationUnchanged; bool isRotate = rotation & DisplayState::eOrientationSwapMask; bool sfSwap = ((mDisplayInstallOrientation & DisplayState::eOrientationSwapMask) && isPrimary())? true:false; int w, h; int frameWidth, frameHeight; w = isRotate ? height : width; h = isRotate ? width : height; // look for LogicalDisplay.java setProjectionLocked if (w * overrideHeight < h * overrideWidth) { frameWidth = w; frameHeight = overrideHeight * w / overrideWidth; } else { frameWidth = overrideWidth * h / overrideHeight; frameHeight = h; } frame.set(Rect((w - frameWidth) / 2, (h - frameHeight) / 2, (w + frameWidth) / 2, (h + frameHeight) / 2)); if (sfSwap) { std::swap(frame.left, frame.top); std::swap(frame.right, frame.bottom); }}void DisplayDevice::setProjection(int orientation, const Rect& newViewport, const Rect& newFrame) { int override_width = 0, override_height = 0; char buf[PROPERTY_VALUE_MAX]; char *val; buf[0] = '\0'; if (mIsPrimary) { property_get("persist.display.size_force", buf, ""); if (buf[0] == '\0') { property_get("ro.config.size_override", buf, ""); } } if (buf[0] == '\0') { frame = Rect(w, h); } else { val = strtok(buf, ","); if (val != NULL) { override_width = atoi(val); } val = strtok(NULL, ","); if (val != NULL) { override_height = atoi(val); } getOverrideFrame(w, h, override_width, override_height, frame); } if (mDefaultRotation & DisplayState::eOrientationSwapMask) { std::swap(frame.left, frame.top); std::swap(frame.right, frame.bottom); } if (sfSwap) { std::swap(frame.left, frame.top); std::swap(frame.right, frame.bottom); } } if (viewport.isEmpty()) { // viewport can be invalid if it has never been set, in that case // we assume the whole display size. // it's also invalid to have an empty viewport, so we handle that // case in the same way. if (override_height != 0 && override_width != 0) { if (sfSwap) viewport = Rect(override_height, override_width); else viewport = Rect(override_width, override_height); } else { viewport = Rect(w, h); } if (mDefaultRotation == DisplayState::eOrientation90 || mDefaultRotation == DisplayState::eOrientation270) { std::swap(viewport.right, viewport.bottom); if (R.getOrientation() == ui::Transform::ROT_0 || R.getOrientation() == ui::Transform::ROT_180 || sfSwap) { // viewport is always specified in the logical orientation // of the display (ie: post-rotation). std::swap(viewport.right, viewport.bottom); } } else { if (R.getOrientation() & ui::Transform::ROT_90 || sfSwap) { // viewport is always specified in the logical orientation // of the display (ie: post-rotation). std::swap(viewport.right, viewport.bottom); } } } ……}
去掉systemui/wallpaper后延迟30s到主界面
// frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java在WMS.enableScreenAfterBoot中,当系统Boot后,会发出一个BOOT_TIMEOUT的message,是用于在Boot之后30s内动画还没退出时强行介绍并进入主界面: public void enableScreenAfterBoot() { synchronized (mGlobalLock) { if (mSystemBooted) { return; } mSystemBooted = true; mH.sendEmptyMessageDelayed(H.BOOT_TIMEOUT, 30 * 1000); } }在WMS.performEnableScreen中,会根据当前的状态来判断是否可以结束开机动画: private void performEnableScreen() { synchronized (mGlobalLock) { if (mDisplayEnabled) { return; } if (!mSystemBooted && !mShowingBootMessages) { return; } if (!mShowingBootMessages && !mPolicy.canDismissBootAnimation()) { return; } // Don't enable the screen until all existing windows have been drawn. if (!mForceDisplayEnabled // TODO(multidisplay): Expand to all displays? && getDefaultDisplayContentLocked().checkWaitingForWindows()) { return; } ……去掉systemui和壁纸会导致条件不满足,具体可看canDismissBootAnimation和checkWaitingForWindows两个方法。
去掉settings起不来
缺少FallbackHome
Android在启动过程中,launcher启动前会启动其他的HOME Activity,原因是此时处于安全模式,无法启动费directBootAware的应用,而settings则支持directBootAware。
设置默认输入法/是能输入法
设置默认输入法:修改frameworks源码,增加字段配置
settings put secure enabled_input_methods xxxxxx
设置永不休眠
设置灭屏时间为一个超大的数值
<integer name="def_screen_off_timeout">2147483647</integer>
设置power键短按就进入关机流程
设置globalAction 长按时间较短即可
<integer name="config_globalActionsKeyTimeout">10</integer>
打开ld(loadLibrary)打印
debug.ld.all=1
允许应用访问test api
am compat enable ALLOW_TEST_API_ACCESS {packageName}
settings put global hidden_api_policy 1
常用命令及原理
su
Android su的源码在system/extras/su目录下,其原理如下:1.在shell中调用su命令是,shell进程fork了一个新的进程去执行su的操作2.调用setgid和setuid方法,设置su进程为root进程。3.执行新的命令,默认为/system/bin/sh,此时终端窗口已经为另一个进程。可通过"echo $$"的来打印当前进程pid,可以发现su前后进程pid已经发生变化,也就是说su前后是处于不同的进程了。
打开pointer location
settings put system pointer_location 1
打开虚拟屏
settings put global overlay_display_devices 1280x720/213
uiautomator抓取界面布局数据
adb shell uiautomator dump XXXXX.uix
截屏 screencap -p xxxx.png
使用AndroidStudio的SDK platform-tools中的uiautomatorviewer.bat打开上面两个文件
shell/python
批量替换文件字符
单独替换一个文件
vim: %s/old/new/g
正则
notepad++提取字符串
原文: Line 34: <TestCase name="android.view.animation.cts.AnimatorInflaterTest">提取:android.view.animation.cts.AnimatorInflaterTest正则:.*?<TestCase name="(.*?)".*输出:\1
sort
查找两次模块测试哪个类型的test没有测试
1.先通过notepad++提取所有的测试用例名,将其保存到文件中,得到两个文本文件。2.在Linux shell中使用sort进行排序(sort -r,表示以ASCII码反向排序,无参以ASCII码正向排序):cat test1.txt | sort > test1.xml3.vimdiff查看差异
XML解析:获取CTS测试用例名
from xml.dom.minidom import parseimport osfile_name = "./test_result.xml"subNodeName = "TestCase"smallNodeName = "Test"class TestCase: def __init__(self, name): self.name = name self.testCases = [] def addCase(self, case): self.testCases.append(case) self.testCases.sort() allTests = []def writeAllTest():fp = open("./test1.txt", "w") for tmp in allTests: fp.write(tmp.name + "\n") for tc in tmp.testCases: fp.write("\t\t" + tc + "\n")def cmpTest(test1, test2): return cmp(test1.name, test2.name)def readXML(): domTree = parse(file_name) rootNode = domTree.documentElement print rootNode subNodes = rootNode.getElementsByTagName(subNodeName) for subNode in subNodes: if subNode.hasAttribute("name"): TestCaseName = subNode.getAttribute("name") #print TestCaseName test = TestCase(TestCaseName) nodes = subNode.getElementsByTagName(smallNodeName) for node in nodes: if node.hasAttribute("name"): testName = node.getAttribute("name") #print "get Test:" + testName test.addCase(testName) allTests.append(test) allTests.sort(cmpTest) writeAllTest() if __name__ == '__main__': readXML()
Makefile
赋值
=
最基本的赋值,变量的值是整个Makefile展开后的结果
:=
覆盖之前的赋值,变量的值取决去当前位置
?=
如果前面没有被赋值过,则使用该值,否则视作不生效
+=
追加值,应该属于列表数据。
常用函数
linux
内核留后门获取root
setuid(0)的类似方法但setuid这个syscall在内核中会去判断权限,因此仿照setuid新增一个方法,然后创建一个procfs节点,提供方式获取root:kernel/sys.c:static int run_as_root(void){ struct user_namespace *ns = current_user_ns(); const struct cred *old; struct cred *new; int retval; kuid_t kuid; kuid = make_kuid(ns, 0); if (!uid_valid(kuid)) return -EINVAL; new = prepare_creds(); if (!new) return -ENOMEM; old = current_cred(); retval = -EPERM; new->suid = new->uid = kuid; if (!uid_eq(kuid, old->uid)) { retval = set_user(new); if (retval < 0) goto root_error; } new->fsuid = new->euid = kuid; retval = security_task_fix_setuid(new, old, LSM_SETID_ID); if (retval < 0) goto root_error; return commit_creds(new);root_error: abort_creds(new); return retval;}static ssize_t uidroot_write(struct file *file, const char __user *buffer, size_t count, loff_t *pos){ char buf[12]; int ret; if (count > 12) return -EFAULT; if (copy_from_user(buf, buffer, count)) { printk("uidroot_write failed\n"); return -EFAULT; } buf[count] = '\0'; printk("uidroot_write user count = %d key = %s, cmp = %d\n", count, buf, strcmp(buf, "runasroot")); if (strncmp("runasroot", buf, 9)) { printk("key not match : %s\n", buf); } else { printk("key match, run as root!\n"); ret = run_as_root(); printk("run as root = %d\n", ret); } return count;}static const struct file_operations proc_uidroot_operations = { .write = uidroot_write,};static int __init proc_uidroot_init(void){ proc_create("uidroot", 0666, NULL, &proc_uidroot_operations); return 0;}__initcall(proc_uidroot_init);测试程序:#include <stdio.h>#include <string.h>#include <stdlib.h>#include <errno.h>#include <unistd.h>#include <sys/types.h>#include <fcntl.h>#include <sys/stat.h>int main(int argc, char *argv[]){ int ret = 0; int fd; printf("uid = %d, euid = %d\n", getuid(), geteuid()); if (remove("/data/local/test.txt")) { printf("remove failed : %s\n", strerror(errno)); } sleep(5); fd = open("/proc/uidroot", O_WRONLY); if (fd < 0) { printf("canot open /proc/uidroot : %s\n", strerror(errno)); } else { write(fd, "runasroot", 10); } /* if (setuid(0) == -1) printf("setuid failed : %s\n", strerror(errno)); */ printf("uid = %d, euid = %d\n", getuid(), geteuid()); if (remove("/data/local/test.txt")) { printf("remove failed : %s\n", strerror(errno)); } sleep(5); return 0;}
驱动中的一些案例
震动马达开关电超时异常
log
[ 144.135506] BUG: scheduling while atomic: vibrator@1.0-se/1843/0x00000002[ 144.143084] Modules linked in: gslX680new xr829 pvrsrvkm(O) xradio_btlpm vin_v4l2 dw9714_act actuator gc0409_mipi ov5648_shenchenxing vin_io videobuf2_v4l2 videobuf2_dma_contig videobuf2_memops videobuf2_core[ 144.163835] Preemption disabled at:[<ffffff800875c8d8>] vibrator_enable+0x30/0x160[ 144.172306] [ 144.173966] CPU: 2 PID: 1843 Comm: vibrator@1.0-se Tainted: G O 4.9.170 #110[ 144.183101] Hardware name: sun50iw10 (DT)[ 144.187568] Call trace:[ 144.190295] [<ffffff800808c038>] dump_backtrace+0x0/0x2b8[ 144.196333] [<ffffff800808c314>] show_stack+0x24/0x30[ 144.201973] [<ffffff8008479be0>] dump_stack+0x90/0xb0[ 144.207611] [<ffffff80080db42c>] __schedule_bug+0x74/0xc0[ 144.213628] [<ffffff8008b30d98>] __schedule+0x658/0x7c0[ 144.219471] [<ffffff8008b30f3c>] schedule+0x3c/0xa0[ 144.224914] [<ffffff8008b35328>] schedule_timeout+0x1b8/0x450[ 144.231342] [<ffffff80087761cc>] sunxi_i2c_xfer+0x5b4/0xed8[ 144.237555] [<ffffff800876cc64>] __i2c_transfer+0x12c/0x590[ 144.243765] [<ffffff800876d134>] i2c_transfer+0x6c/0xf0[ 144.249601] [<ffffff80086083f4>] regmap_i2c_read+0x7c/0xc8[ 144.255734] [<ffffff8008602550>] _regmap_raw_read+0xd0/0x290[ 144.262055] [<ffffff8008602750>] _regmap_bus_read+0x40/0x78[ 144.268271] [<ffffff8008601aec>] _regmap_read+0x6c/0x188[ 144.274192] [<ffffff8008601c58>] regmap_read+0x50/0x78[ 144.279919] [<ffffff8008561218>] regulator_is_enabled_regmap+0x40/0xb8[ 144.287221] [<ffffff800855c630>] _regulator_is_enabled.part.1+0x30/0x50[ 144.294601] [<ffffff800855d654>] regulator_is_enabled+0x74/0x80[ 144.301226] [<ffffff800875c878>] set_sunxi_vibrator+0x90/0xc0[ 144.307635] [<ffffff800875c9ac>] vibrator_enable+0x104/0x160[ 144.313958] [<ffffff800885b4c8>] enable_store+0x68/0xa0[ 144.319796] [<ffffff80085d807c>] dev_attr_store+0x44/0x60[ 144.325822] [<ffffff80082d1aa4>] sysfs_kf_write+0x5c/0x78[ 144.331841] [<ffffff80082d0b50>] kernfs_fop_write+0xc0/0x1d8[ 144.338154] [<ffffff8008237f08>] __vfs_write+0x60/0x158[ 144.343990] [<ffffff8008239018>] vfs_write+0xb0/0x1c0[ 144.349627] [<ffffff800823a6c4>] SyS_write+0x6c/0xd8[ 144.355165] [<ffffff8008083540>] el0_svc_naked+0x34/0x38
异常代码
static void vibrator_enable(struct timed_output_dev *dev, int value){ unsigned long flags; spin_lock_irqsave(&vibe_lock, flags); if (vibe_state == 1) { dprintk(DEBUG_DATA_INFO, "cancle vibe_timer"); vibe_state = 0; set_sunxi_vibrator(vibe_state); } hrtimer_cancel(&vibe_timer); if (value <= 0) vibe_state = 0; else { value = (value > 15000 ? 15000 : value); vibe_state = 1; hrtimer_start(&vibe_timer, ktime_set(value / 1000, (value % 1000) * 1000000), HRTIMER_MODE_REL); } spin_unlock_irqrestore(&vibe_lock, flags); schedule_work(&vibrator_work);}
异常时操作
该驱动是一个非常简单的设备驱动,向用户层透露一个enable节点,往enable节点中写入一个非0值则表示启动一个定时器去打开震动马达的电源,使马达震动。其中当前一次的定时器没结束时,则先取消前一次定时器并关闭马达电源。异常操作时是连续输入两个1000以上的值,如"echo 2000 > enable && echo 1000 > enable"。
分析
从异常的堆栈来看,是在第二次写入值时触发访问电源是否打开的操作时异常,其异常是说regulator去使用i2c进行访问pmu时出现schedule超时了,所以怀疑是此时cpu关闭掉调度了。再看回vibrator_enable的操作,其中有一个令人迷惑的语句:spin_lock_irqsave,在驱动中并没有使用中断,且spin_lock_irqsave会关闭本地中断,因此怀疑此处将系统的调度都关闭了。使用mutex来替换该操作后驱动变正常。
hexo
指定子目录创建文件
hexo n "${name}" -p "${dir}/${name}"
主题