Android多用户模式下获取当前UserId的方式

1. Linux uid/gid

Linux下的用户id(uid)和群组id(gid)。Linux是多用户系统,每个用户都拥有一个uid,这个uid由系统和用户名做映射绑定。同时,为了便于用户管理(譬如管理文档权限),Linux引入了群组的概念,可以将多个用户归于一个群组。每一个群组拥有一个群组id(gid)。 
root用户:Linux下的唯一的超级用户,拥有所有的系统权限。root用户所在的组就是root组。

2. Android uid(4.2(API Level 17))

Android 4.2开始支持多用户。Linux的uid/gid多用户体系已经被用在App管理上。

Android重新开发了一套多用户体系,在UserManagerService中管理,PackageManagerService和ActivityManagerService中也有相关逻辑。Android的多用户可以做到不同用户的应用的物理文件级(数据)的区分,以实现不同用户有不同的壁纸、密码,以及不同的应用等。

例如:在一个有两个用户(用户id分别为0和10)的安卓设备上,在用户10下安装一个应用,此时,在0下是看不到这个应用的。

从data/system/packages.xml查看此应用的uid:userId=”10078”

Process.myUid()得到uid为”1010078”

Process.myUserHandle()得到”userHandle{10}”

在另一个用户0下安装此应用。

查看packages.xml,看到uid没有变化10078

Process.myUid()得到uid为”10078”

Process.myUserHandle()得到”userHandle{0}”

adb shell进入命令行,分别查看data/user/0和data/user/10下面此应用的数据区:

用户0: 

 

用户10:

 

可以看到,实际上应用在内部虽然有多用户,但只有一个uid,在不同的用户下,通过uid和用户id合成一个新的uid,以保证在每个用户下能够区分。 

(可以看到文件拥有者是u0_a78,所在群组为u0_a78。从data/system/packages.xml根据包名查看此应用信息,可以看到:userId=”10078”。)

3. android.os.UserHandle

这个类对外提供有关多用户的接口。 从里面的一些api代码可以看到uid在多用户下的处理逻辑:

多用户支持开关: 

注意一个api getUid()。这就清楚了,将用户id 10作为第一个参数,packages.xml中记录的该应用的uid 10078作为第二个参数传入,得到了这个应用在10用户下的uid——1010078! 


 

通过应用的uid得到当前用户的userId,以上过程的逆过程: 

 

从另一个核心的api myUserId()更能清楚地看到应用uid和用户id的关系: 

 

当一个应用使用UserHandle.myUserId()来获取当前的用户id的时候,其实就是从他自己的进程得到应用的uid,然后通过上述逻辑计算出当前的用户id。 

从Process.myUserHandle()也能清楚地看到这个逻辑:

从概念和API命名上,确实有些混乱,但Android也情非得已,Process的API Level是1,UserHandle的API Level是17,可见在最初的Android上面,已经将Linux uid/gid给了应用id了,当时应该也没有考虑Android有一天需要支持多用户。直到4.2(API Level 17),引入了多用户时,已经是若干年过去了,Process已经被无数的开发者使用,无法改变。只能接受这个概念上混淆了。 

可以用如下的几点来简单地澄清这些id概念: 

  1. Process中的xxid相关的概念和API是关于应用id的。 
  2. UserHandle中的xxid相关的概念和API是关于Android用户id的。 
  3. Process有接口得到UserHandle实例。

4. 应用层获取UserId

有时候,我们需要根据不同的用户ID来进行兼容性处理,比如魅族的系统在分身模式下,生物识别相关的Keystore调用(setUserAuthenticationRequired(true))会抛出异常。

我们需要针对这种情况进行兼容性处理,已知的是,魅族的应用分身模式下,用户的ID一定是 999

Process.myUserHandle() 可以得到 UserHandle 对象,但是却不能直接从 UserHandle 对象中获取到用户ID

目前有三种已知的做法

  1. android.os.Process.myUid()/100000 这行代码的原理是依据 Process.myUserHandle() 实现的代码进行逆向操作来获取到真正的用户ID,如果不了解源代码的人会感觉莫名其妙。不需要特殊权限,但是总感觉不够优雅。

  2. ActivityManager.getCurrentUser() 需要申请权限,需要系统应用,需要反射调用。感觉更不优雅了。

  3. UserHandle.myUserId() 不需要特殊权限,需要反射调用。这个感觉也不够优雅。但是当看到androidx.os.UserHandleCompat 也是通过反射调用这些函数的时候,瞬间感觉无所谓了。
    代码参考如下:

参考链接


Windows 10系统VirtualBox无法进入系统,日志报错“HM: HMR3Init: Attempting fall back to NEM: VT-x is not available”

Windows 10上使用 Linux 子系统的时候,无法成功启用。根据官方文档 旧版 WSL 的手动安装步骤 之后,依旧没效果,反倒是VirtualBox无法进入系统了。

观察日志,报错信息如下:

Intel CPU

AMD CPU

解决方案如下:

1.使用管理员启动命令行.

2. 执行如下命令:


某些电脑需要额外执行如下命令:


3. 重启电脑

参考链接


GTX760支持4k分辨率吗?

支持的,所有开普勒架构的桌面显卡,都可以支持到4K分辨率的输出,包括GTX650等极端开普勒架构显卡。

以下是GTX760最新的官方参数

从技术支持和特性描述可以看出,GTX760原生支持4K输出,当然也就包括了各个子生厂商生产出的各种GTX760。并且可以通过DP接口输出4K@60Hz,通过HDMI输出4K@24Hz~30Hz,HDMI下的刷新率不如DP是因为HDMI的版本带来的带宽问题,所以如果你连接的是60Hz刷新率的4K显示器,那么建议是用DP接口进行连接。

之所以一些品牌的GTX760没写支持4K,是因为在GTX760发布的时候,4K还并不盛行,所以一些产品的资料里就并没注明支持4K,后来这些资料也并未进行更新,所以可能会产生误导,实际上是支持的。

只是GTX760的4K游戏性能较弱,如果你打算用GTX760在4K下进行游戏,那么可能需要调低不少特效,才能保证一定的画面流畅度。

参考链接


微星gtx760支持4k分辨率吗?

ubuntu 20.04.4系统升级后全部应用图标都无法显示

ubuntu 20.04.4系统执行升级命令后全部应用图标都无法显示,系统变成如下图:

很多应用也无法打开了,点击之后无任何反应。

查看系统日志可以看到如下错误信息:

这个原因是由于 gdk-pixbuf 的改动导致的系统 BUG,只需要重新生成一下应用图标缓存即可。

执行如下命令:

参考链接

 

解决processDebugAndroidTestManifest Attribute meta-data#XXX@value requires a placeholder substitution but no value is provided.

在集成极光推送 SDK 的时候,如果把极光推送集成到一个独立的 AAR 包中,在编译的时候会发生如下报错:

如果是构建单一 APK 的情况下,可以直接在 AAR 项目里面通过 manifestPlaceholders 直接配置这些参数。

但是如果是多渠道打包的情况下,我们会在统一的地方根据不同的渠道进行配置。

这个时候直接在 AAR 项目里面配置就不合适了,此时我们观察报错信息,会发现其实真正报错的地方是执行单元测试的时候报错(processDebugAndroidTestManifest)。

其实也容易理解,执行 AndroidTest 的时候,需要生成单独测的测试 APK,这一步就必须补充完整全部的 meta-data 信息,否则无法顺利生成 APK

通过上述的分析,解决这个问题就比较简单了,我们只需要在测试用例的时候补充这些 meta-data 信息即可。

那接下来就是怎么生成 AndroidTest 的时候的配置信息,网上搜索很久,才在 Possibility to define manifestPlaceholders or BuildConfig variables just for tests 找到相关的解决方案。

参考解决方法如下:

极光推送相关的解决参考如下:

参考链接


新一代UI框架-Flutter的单元测试方法

1、Flutter是什么

Flutter是谷歌的移动UI框架,运用Dart语言,可以快速在iOS和Android上构建高质量的原生用户界面。通俗地来讲,Flutter是一款移动应用程序SDK,一份代码可以同时生成iOS和Android两个高性能、高保真的应用程序。Flutter目标是使开发人员能够交付在不同平台上都感觉自然流畅的高性能应用程序。我们兼容滚动行为、排版、图标等方面的差异。

Flutter为什么选择Dart

1、Dart 的性能更好。 js或dart都是一种声明式的写法,但js需要解释,dart是直接语言层面支持了持了node tree的书写,且对象创建成本低,可直接编译成native代码(AOT),VM效率更高,所以运行上dart效率会很多, 而且dart 是一种同时支持 JIT/AOT 编译的语言,JIT 开发模式时能快速编译生效,是Hot Reload体验的关键。速度的提升对高帧率下的视图数据计算很有帮助。

2、Native Binding。在 Android上,v8的 Native Binding可以很好地实现,但是 iOS上的JavaScriptCore不可以,所以如果使用 JavaScript,Flutter 基础框架的代码模式很难统一。而Dart的 Native Binding可以很好地通过 Dart Lib实现。

3、Dart是类型安全的语言,拥有完善的包管理和诸多特性。Google召集了如此多个编程语言界的设计专家开发出这样一门语言,旨在取代 JavaScript,所以 Fuchsia OS内置了 Dart。Dart可以作为 embedded lib嵌入应用,而不用只能随 着系统升级才能获得更新,这也是优势之一。

Flutter的优点

1、提高开发效率

同一份代码开发iOS和Android用更少的代码做更多的事情轻松迭代 在应用程序运行时更改代码并重新加载(通过热重载)修复崩溃并继续从应用程序停止的地方进行调试

2、创建美观,高度定制的用户体验

受益于使用Flutter框架提供的丰富的Material Design和Cupertino(iOS风格)的widget实现定制、美观、品牌驱动的设计,而不受原生控件的限制

2、Flutter的测试方案

根据flutter.dev介绍,Flutter测试方案可分为三种:

单元测试 测试单一功能、方法或类。例如,被测单元的外部依赖性通常被模拟出来,如package:mockito。 单元测试通常不会读取/写入磁盘、渲染到屏幕,也不会从运行测试的进程外部接收用户操作。单元测试的目标是在各种条件下验证逻辑单元的正确性。

wiget测试 在其它UI框架称为 组件测试) 测试的单个widget。测试widget涉及多个类,并且需要提供适当的widget生命周期上下文的测试环境。 例如,它应该能够接收和响应用户操作和事件,执行布局并实例化子widget。widget测试因此比单元测试更全面。 然而,就像一个单元测试一样,一个widget测试的环境被一个比完整的UI系统简单得多的实现所取代。小部件测试的目标是验证小部件的UI如预期的那样的外观和交互。

集成测试

测试一个完整的应用程序或应用程序的很大一部分。通常,集成测试可以在真实设备或OS仿真器上运行,例如iOS Simulator或Android Emulator。 被测试的应用程序通常与测试驱动程序代码隔离,以避免结果偏差。集成测试的目标是验证应用程序作为一个整体正确运行,它所组成的所有widget如预期的那样相互集成。 您还可以使用集成测试来验证应用的性能。

3、编写Flutter的单测环境与case

创建一个Flutter的单测case,主要分以下四个步骤:

  • 创建一个被测方法
  • 引入Flutter Test Library
  • 创造flutter单元测试用例
  • 注入并执行单测case

创建被测方法 如图所示,首先编写一个简单的测试方法calc

引入Flutter Test Library 接着,需要在配置文件pubspec.yaml文件中引入对应的测试library,从而保证在测试时这个dependency可以被引入

创造flutter单元测试用例 在Module的目录下,新创建一个目录,下面放我们编写的单测用例,我们将被测用例命名为test.dart

执行用例 写一个main方法作为入口,在终端键入命令flutter test运营测试,可以看到,我们的测试用例未通过,原因是expect方法预期结果与实际结果不同导致。

这样,一个完整的flutter单元测试就完成了。

4、总结

总结来说,作为一个经验法则,单元测试虽然执行速度快,依赖少,但能给项目带来的质量信心是最低的;经过充分测试的应用程序应该具有非常多的单元和widget测试,通过代码覆盖(code coverage)进行跟踪,以及覆盖所有重要使用场景的大量集成测试,才可从各阶段、各方面保证新产品的质量品质。

参考链接


新一代UI框架-Flutter的单元测试方法

像素坐标转世界坐标的计算

原理

下图表示了小孔成像模型(图片及公式参考 OpenCV官方资料

这个图里涉及4个坐标系:

  1. 世界坐标系:其坐标原点可视情况而定,可以表示空间的物体,单位为长度单位,比如MM(毫米),用矩阵[XWYWZW]表示;
  2. 相机坐标系:以摄像机光心为原点(在针孔模型中也就是针孔为中心),z轴与光轴重合,也就是z轴指向相机的前方(与成像平面垂直),x轴与y轴的正方向与世界坐标系平行,单位为长度单位,比如MM(毫米),用矩阵[XcYcZc]表示;
  3. 图像物理坐标系(也叫成像平面坐标系):用物理长度单位表示像素的位置,坐标原点为摄像机光轴与图像物理坐标系的交点位置,单位为长度单位,比如MM(毫米),用矩阵[xy]表示。
  4. 像素坐标系:坐标原点在左上角,以像素为单位,有明显的范围限制,即用于表示全画面的像素长和像素长宽,矩阵[uv]表示。

以下公式描述了[uv]T[xy]T[XcYcZc]T[XWYWZW]T之间的转换关系。

z[uv1]=[1/dx0cx01/dycy001][f000f0001][r11r12r13t1r21r22r23t2r31r32r33t3][XWYWZW1]

以上公式中,dxdy表示1个像素有多少长度,即用传感器的尺寸除以像素数量,比如2928.384um * 2205.216um的传感的分辨率为2592 * 1944,每个像素的大小即约1.12um。

由于相机与物体的视角来看,都是三维坐标,因此两者之间的变换只需要进行矩阵的旋转、平移即可达到坐标系转换的目的(不同坐标系中,物体的绝对大小并不会随着坐标系的变化而变化,因此不涉及缩放处理)。对于变换矩阵  [r11r12r13t1r21r22r23t2r31r32r33t3] 需要理解,矩阵是由 3*3 的旋转矩阵 r (rotation) 和 3*1的平移向量 t (translation)组成。

f表示焦距,在上图中,根据相似三角形,P点和p点具有以下关系:

Xcx=Ycy=Zcfx=Xc/(Zcf) y=Yc/(Zcf),可见:f越大,xy越大,Zc越大,xy越小。

cxcy表示中心点在像素坐标系中的位置。

要求像素坐标系中某像素点对应在世界坐标系中的位置,需要知道相机的内参、外参,相机的内参可以通过标定获得,外参可以人为设定。

第一步,将像素坐标变换到相机坐标系:

z[uv1]=[fx0cx0fycy001][xy1]=K[xy1]

两边乘以K的逆后推导出:

[xyz]=K1[uv1]

第二步,从相机坐标系变换到世界坐标系:

[XcYcZc]=R[XYZ]+t

将方程乘以R1,可以推导出:

[XYZ]=[XcYcZc]R1tR1=z[xy1]R1tR1

代码

通过输入相机的内参,旋转向量,平移向量和像素坐标,可以通过以下函数求出对应的世界坐标点。
以下代码中需求注意要对平移向量取转置,将1x3矩阵变为3x1矩阵后,才能实现3x3矩阵和3x1矩阵的乘法运算。

验证

先使用projectPoints生成像素点:

使用以下欧拉角:

对应的平移向量,表示空间坐标原点相对在相平面原点偏移x=134mm,y=132mm,z=200mm。

生成空间坐标点:

经projectPoints计算后对应的像素空间点是:

经函数求出的空间坐标点是:

可以对比按11*8格和30mm/格所生成空间坐标点结果,基本一致。

参考链接


Missing accessibility label: provide either a view with an `android:labelFor` that references this view or provide an `android:hint`

Android Studio 2021.2.1 新定义 layout 文件的时候,如果 EditTextTextView 相邻定义,如下:

会在 EditText 收到警告信息:

或者:

这个警告的原因是:一般情况下,EditTextTextView 相邻的时候,TextView 一般用于提示用户应该输入何种内容,尤其是有多个 EditTextTextView 对的时候,可以通过指定 android:labelFor  来指出两者的对应关系。也可以通过给每个 EditText 增加一个 android:hit 方便用户理解。

解决方法如下:

或者:

参考链接


Difference between android:id and android:labelFor?

摄像头和机械臂的手眼标定

一、背景

Calibration是机器人开发者永远的痛。虽然说方法说起来几十年前就有,但每一个要用摄像头的人都还是要经过一番痛苦的踩坑,没有轻轻松松拿来就效果好的包。其实人类不就是个手眼协调的先进“机器人”吗,O(∩_∩)O哈哈~

机器人视觉应用中,手眼标定是一个非常基础且关键的问题。简单来说手眼标定的目的就是获取机器人坐标系和相机坐标系的关系,最后将视觉识别的结果转移到机器人坐标系下。

手眼标定行业内分为两种形式,根据相机固定的地方不同,如果相机和机器人末端固定在一起,就称之为“眼在手”(eye in hand),如果相机固定在机器人外面的底座上,则称之为“眼在外”(eye to hand)。

eye to hand 眼在外
eye to hand 眼在外
eye to hand 眼在手
eye to hand 眼在手

二、手眼关系的数学描述

1. eye in hand,这种关系下,两次运动,机器人底座和标定板的关系始终不变。求解的量为相机和机器人末端坐标系的位姿关系。

2. eye to hand,这种关系下,两次运动,机器人末端和标定板的位姿关系始终不变。求解的量为相机和机器人底座坐标系之间的位姿关系。

手眼标定eye in hand 和eye to hand 的区别主要是机器人那边,一个是end相对于base,另一个是base相对于end。千万注意。

三、AX = XB问题的求解

旋转和平移分步法求解:

  • Y. Shiu, S. Ahmad Calibration of Wrist-Mounted Robotic Sensors by Solving Homogeneous Transform Equations of the Form AX = XB. In IEEE Transactions on Robotics and Automation, 5(1):16-29, 1989.
  • R. Tsai, R. Lenz A New Technique for Fully Autonomous and Efficient 3D Robotics Hand/Eye Calibration. In IEEE Transactions on Robotics and Automation, 5(3):345-358, 1989.

迭代求解及相关资料可以看看相关网上的英文教程 Calibration and Registration Techniques for Robotics 其中也有一些AX= XB的matlab代码可以使用。

ROS 下也有相关的一些package可以利用

四、其他参考资料

3D 视觉之手眼标定 邱强Flyqq 微信文章

手眼标定的两种方式_wzj5530的专栏-CSDN博客_手眼标定 图不错

深入浅出地理解机器人手眼标定_二毛的博客-CSDN博客_机器人手眼标定 部分halcon代码

eye-in-hand手眼标定系统_二毛的博客-CSDN博客 halcon代码

手眼标定之9点法_GoRunningSnail的博客-CSDN博客 部分原理

UR5、Kinect2手眼标定总结_zhang970187013的博客-CSDN博客 UR5 与easy hand eye

一般用“两步法”求解基本方程,即先从基本方程上式求解出旋转部分,再代入求解出平移部分。

经典手眼标定算法之Tsai-Lenz的OpenCV实现_YunlinWang的博客-CSDN博客

============== Halcon 官方示例-手眼标定 ==================

五、Matlab下手眼标定解算

相机与机器人是eye-to-hand模式,机器人为加拿大Kinova 6轴机械臂,机器人pose为基座相对于末端的x,y,z,rx,ry,rz,rw, 单位为米。姿态使用单位四元数表示。

2017.08.29Kinova_pose_all_8_1.txt

pattern pose为标定板相对于相机的x,y,z,rx,ry,rz,rw, 单位为米。姿态使用单位四元数表示。

2017.08.29Pattern_pose_all_8_1.txt

此Matlab文件调用数据进行离线解算。Calibration and Registration Techniques for Robotics 的这部分 Registering Two Sets of 6DoF Data with 1 Unknown,有code下载,下载好命名为shiu.m和tsai.m供下面程序调用就行。我这里贴出

Jaco_handeye_test_10.m 测试程序中用到了Peter Corke老师的机器人工具箱。我的Matlab版本R2013a,利用机器人工具箱的一些转换函数(四元数的构建,欧拉角转换等),它安装和基本使用参考这里:Matlab机器人工具箱_Learning by doing-CSDN博客_matlab机器人工具箱


稍微解释一下,程序做的就是读入机器人和相机的两两姿态信息,转换为4x4 的齐次变换矩阵,送入tsai.m程序求解。

手眼标定eye in hand 和eye to hand 的区别主要是机器人那边,一个是end相对于base,另一个是base相对于end。千万注意。

====================平面九点标定法====================

当利用RGB相机或者是机器人只进行平面抓取(也即固定姿态抓取,机器人六自由度位置和姿态简化为只考虑平移,姿态人为给定并且固定,这时机器人可以移动到目标点上方),问题可以简化为平面RGB图像的目标像素点集A(x1,y1)与机器人在平面(X1,Y1)的点对关系。具体做法是相机识别像素点给到A,然后利用示教器查看机器人在基座标系下的坐标,当做B。

相机坐标和机器人坐标写成齐次的形式,投影矩阵X是一个3x3的矩阵我们需要6组对应点来求解最小配置解。利用奇异值分解SVD来求取。

D:\opencv_work\cubeSolver\cv_solver\ConsoleApplication1\CV_SVD.cpp

D:\Matlab_work\handeye\NinePoints_Calibration.m

OpenCV: Operations on arrays

结果对比:左halcon C#(第三列为0,0,1,没做显示),右opencv c++,底下为Matlab结果,三者一致,算法检测通过。

=============

四轴平面机器人的手眼标定_Stones1025的博客-CSDN博客

这种方法利用点对,求仿摄变换矩阵

================= Eye in hand 数据及Ground truth =========================

Marker in Camera 八组数据,单位:米及弧度,姿态用的是RotVector表示

Robot end-effector in Base 八组数据,单位:米及弧度,姿态用的是RotVector表示

Ground truth:Camera in end-effector

参考链接


理解Intellj IDEA/Android Studio警告'Optional' used as type for parameter

在函数的形参中使用 Optional 类型的参数的时候,编译的时候会被 Intellj IDEA/Android Studio 发出警告,代码如下:

警告信息如下:

对于这个警告,初期是非常迷惑的,不清楚为什么 Optional 类型不能作为函数的形参。后来搜索了一下网络,加上自己理解,才豁然开朗。

其实 Optional 类型产生的本身是为了避免空指针异常而引入的,如果在函数的形参中使用 Optional 类型,那么如果传入的 Optional 类型的参数本身就是 null 的话,就会在使用 Optional 参数的时候抛出空指针异常了。因此干脆就希望不要在函数的行参中使用 Optional 类型。

通过测试,Intellj IDEA/Android Studio 对于函数返回值,是不发出这个警告的。

接口的话,可以使用类似下面的写法来规避这个警告:

虽然可以通过 @SuppressWarnings("OptionalUsedAsFieldOrParameterType") 来禁止这个警告的发出,但是还是建议遵守这个警告,尽量不要在函数的形参中使用 Optional 类型。

参考链接