Android10以上实现获取设备序列号功能

Android10以上实现获取设备唯一标识,目前只支持华为和荣耀设备。实现原理:通过无障碍服务读取序列号界面。

public class DeviceHelper implements Application.ActivityLifecycleCallbacks {

    static final String TAG = "WADQ_DeviceHelper";

    static final String ACTION_ACQUIRE_SERIAL_SUCCESS = "zwxuf.intent.action.ACQUIRE_SERIAL_SUCCESS";

    private static Handler mHandler = new Handler(Looper.getMainLooper());
    private boolean isMsgReceiverEnabled;
    private OnAcquireSerialListener mOnAcquireSerialListener;

    private Activity mActivity;
    private Application mApplication;

    public DeviceHelper(Activity mActivity) {
        this.mActivity = mActivity;
        mApplication = mActivity.getApplication();
        mApplication.registerActivityLifecycleCallbacks(this);
    }

    public void acquireSerial(OnAcquireSerialListener listener) {
        mOnAcquireSerialListener = listener;
        if (!isMsgReceiverEnabled) initMsgReceiver();
        AcquireSerialService.isSerialFound = false;
        AcquireSerialService.isStatusInfoFound = false;
        Intent intent = new Intent(Settings.ACTION_DEVICE_INFO_SETTINGS);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        mActivity.startActivity(intent);
    }

    private void releaseAcquireSerial() {
        List<Service> services = getServices();
        for (Service service : services) {
            if (service instanceof AcquireSerialService) {
                ((AcquireSerialService) service).release();
                break;
            }
        }
    }

    private void initMsgReceiver() {
        IntentFilter filter = new IntentFilter(ACTION_ACQUIRE_SERIAL_SUCCESS);
        mActivity.registerReceiver(mMsgReceiver, filter);
        isMsgReceiverEnabled = true;
    }

    private BroadcastReceiver mMsgReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String serial = intent.getStringExtra("serial");
            if (mOnAcquireSerialListener != null) {
                mOnAcquireSerialListener.onAcquireSerial(serial);
            }
            releaseMsgReciever();
        }
    };

    private void releaseMsgReciever() {
        if (isMsgReceiverEnabled) {
            mActivity.unregisterReceiver(mMsgReceiver);
            isMsgReceiverEnabled = false;
        }
    }

    public void release() {
        releaseMsgReciever();
        mApplication.unregisterActivityLifecycleCallbacks(this);
        releaseAcquireSerial();
    }

    public boolean canAcquireSerial() {
        return isServiceEnabled(mActivity, AcquireSerialService.class);
    }

    public void openAcquireSerialSettings(final int requestCode) {
        Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        try {
            mActivity.startActivityForResult(intent, requestCode);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static boolean isServiceEnabled(Context context, Class<? extends AccessibilityService> serviceClass) {
        if (serviceClass == null) {
            return false;
        }
        String serviceName = context.getPackageName() + "/" + serviceClass.getName();
        try {
            int enabled = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED);
            if (enabled == 1) {
                String service = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
                Log.i(TAG, service);
                return service != null && service.contains(serviceName);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }


    static Handler getHandler() {
        return mHandler;
    }


    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {

    }

    @Override
    public void onActivityStarted(Activity activity) {

    }

    @Override
    public void onActivityResumed(Activity activity) {

    }

    @Override
    public void onActivityPaused(Activity activity) {

    }

    @Override
    public void onActivityStopped(Activity activity) {

    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        if (activity == mActivity) {
            release();
        }
    }

    private static List<Service> getServices() {
        List<Service> services = new ArrayList<>();
        Object mActivityThread = getActivityThread();
        try {
            Class mActivityThreadClass = mActivityThread.getClass();
            Field mServicesField = mActivityThreadClass.getDeclaredField("mServices");
            mServicesField.setAccessible(true);
            Object mServices = mServicesField.get(mActivityThread);
            if (mServices instanceof Map) {
                Map<IBinder, Service> arrayMap = (Map) mServices;
                for (Map.Entry<IBinder, Service> entry : arrayMap.entrySet()) {
                    Service service = entry.getValue();
                    if (service != null) {
                        services.add(service);
                    }
                }
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return services;
    }

    private static Object getActivityThread() {
        try {
            Class ActivityThread = Class.forName("android.app.ActivityThread");
            Method currentActivityThread = ActivityThread.getMethod("currentActivityThread");
            currentActivityThread.setAccessible(true);
            return currentActivityThread.invoke(null);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }

}
public class AcquireSerialService extends AccessibilityService {


    static boolean isStatusInfoFound;
    static boolean isSerialFound;


    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {

        if (event.getPackageName() == null || event.getClassName() == null) {
            return;
        }

        String packageName = event.getPackageName().toString();
        String className = event.getClassName().toString();

        final AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
        if (nodeInfo != null) {
            if (!packageName.equals(getApplicationContext().getPackageName())) {
                enumChildNodeInfo(packageName, nodeInfo, 0);
            }
        }
    }

    @Override
    public void onInterrupt() {

    }

    private Runnable mScrollRunnalbe;

    private void enumChildNodeInfo(String packageName, AccessibilityNodeInfo nodeInfo, int level) {
        int count = nodeInfo.getChildCount();
        if (count > 0) {
            for (int i = 0; i < count; i++) {
                final AccessibilityNodeInfo childNodeInfo = nodeInfo.getChild(i);
                if (childNodeInfo == null) continue;
                if (childNodeInfo.isScrollable()) {
                    childNodeInfo.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
                }
                if (!isSerialFound) {
                    String serial = getSerialByNodeInfo(packageName, childNodeInfo);
                    if (serial != null && !serial.isEmpty()) {
                        //获取到sn
                        Log.i(DeviceHelper.TAG, serial);
                        Intent intent = new Intent(DeviceHelper.ACTION_ACQUIRE_SERIAL_SUCCESS);
                        intent.putExtra("serial", serial);
                        sendBroadcast(intent);
                        performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
                        DeviceHelper.getHandler().postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
                                release();
                            }
                        }, 200);
                        return;
                    }
                    enumChildNodeInfo(packageName, childNodeInfo, level + 1);
                }
            }
        }
        nodeInfo.recycle();
    }


    private String getSerialByNodeInfo(String packageName, AccessibilityNodeInfo nodeInfo) {
        if (nodeInfo.getText() == null) {
            return null;
        }
        String text = nodeInfo.getText().toString();
        if (text.equals("序列号")) {
            isStatusInfoFound = true;
            isSerialFound = true;
            if (mScrollRunnalbe != null) {
                DeviceHelper.getHandler().removeCallbacks(mScrollRunnalbe);
                mScrollRunnalbe = null;
            }
            return getValue(nodeInfo);
        } else if (packageName.equals("com.android.settings") && (text.equals("状态信息") || text.equals("状态消息")) && !isStatusInfoFound) {
            isStatusInfoFound = true;
            AccessibilityNodeInfo statusInfoParent = nodeInfo.getParent();
            while (statusInfoParent != null && !statusInfoParent.isClickable()) {
                statusInfoParent = statusInfoParent.getParent();
            }
            if (statusInfoParent != null) {
                final AccessibilityNodeInfo finalStatusInfoParent = statusInfoParent;
                DeviceHelper.getHandler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        finalStatusInfoParent.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                    }
                }, 200);
            }
        }
        return null;
    }

    private String getValue(AccessibilityNodeInfo nodeInfo) {
        AccessibilityNodeInfo snLayout = nodeInfo.getParent();
        if (snLayout != null) {
            while (true) {
                List<AccessibilityNodeInfo> snSummaryList = snLayout.findAccessibilityNodeInfosByViewId("android:id/summary");
                if (snSummaryList != null && !snSummaryList.isEmpty()) {
                    AccessibilityNodeInfo snSummary = snSummaryList.get(0);
                    if (snSummary != null && snSummary.getText() != null) {
                        return snSummary.getText().toString();
                    }
                }
                snLayout = snLayout.getParent();
                if (snLayout == null) {
                    break;
                }
            }
        }
        return null;
    }


    private String getNodeText(AccessibilityNodeInfo nodeInfo) {
        if (nodeInfo != null && nodeInfo.getText() != null) {
            return nodeInfo.getText().toString();
        } else {
            return null;
        }
    }


    public void release() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            disableSelf();
        }
    }
}

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private DeviceHelper mDeviceHelper;
    private TextView tv_serial, tv_phone;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        tv_serial = findViewById(R.id.tv_serial);

        mDeviceHelper = new DeviceHelper(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.bn_get_serial:
                getSerial();
                break;
        }
    }

    private void getSerial() {
        if (!mDeviceHelper.canAcquireSerial()) {
            new AlertDialog.Builder(this)
                    .setMessage("没有开启无障碍服务")
                    .setPositiveButton("去开启", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            mDeviceHelper.openAcquireSerialSettings(1000);
                        }
                    })
                    .setNegativeButton("取消", null)
                    .create()
                    .show();
            return;
        }
        mDeviceHelper.acquireSerial(new OnAcquireSerialListener() {
            @Override
            public void onAcquireSerial(String serial) {
                //Toast.makeText(MainActivity.this, serial, Toast.LENGTH_SHORT).show();
                tv_serial.setText("sn:" + serial);
            }
        });
    }


}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/771201.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

无人机智能追踪反制系统技术详解

随着无人机技术的飞速发展&#xff0c;无人机在各个领域的应用越来越广泛。然而&#xff0c;无人机的无序飞行和非法使用也带来了一系列安全隐患和威胁。因此&#xff0c;无人机智能追踪反制系统应运而生&#xff0c;成为维护公共安全和防止无人机滥用的重要工具。本文将详细介…

SPI四种模式--极性与相位

SPI的四种模式&#xff1a;相位和极性 极性 定义时钟空闲状态&#xff1a; CPOL0&#xff1a;时钟线在空闲状态为低电平 CPOL1&#xff1a;时钟线在空闲状态为高电平 这个设置决定了设备不进行通信时时钟线的状态。 兼容性&#xff1a; 不同的SPI设备可能需要不同的时钟极性…

Spring Boot 的机场投诉管理平台-计算机毕业设计源码22030

摘要 随着航空运输业的迅速发展&#xff0c;机场的客流量不断增加&#xff0c;旅客对机场服务的质量和效率也提出了更高的要求。为了提高机场的服务质量&#xff0c;及时处理旅客的投诉&#xff0c;建立一个高效、便捷的机场投诉管理平台显得尤为重要。 本项目旨在设计与实现一…

飞利浦的台灯值得入手吗?书客、松下多维度横评大分享!

随着生活品质的持续提升&#xff0c;人们对于健康的追求日益趋向精致与高端化。在这一潮流的推动下&#xff0c;护眼台灯以其卓越的护眼功效与便捷的操作体验&#xff0c;迅速在家电领域崭露头角&#xff0c;更成为了众多家庭书房中不可或缺的视力守护者。这些台灯以其精心设计…

AIGC对设计师积极性的影响

随着科技的迅猛发展&#xff0c;生成式人工智能&#xff08;AIGC&#xff09;工具正逐渐深入设计的每个角落&#xff0c;对设计师的工作方式和思维模式产生了深远的影响。AIGC不仅极大提升了设计师的工作效率&#xff0c;更激发了他们的创新思维&#xff0c;为设计行业带来了翻…

java 基础之 反射技术_java 程序src阶段 class对象阶段 run阶段3个阶段

System.out.println(in); } publicClass[] aa1(String name, int[] password){ returnnew Class[]{String.class} ; } privatestatic void aa1(int num){ System.out.println(num“静态方法”); } public static void main(String[] args){ System.out.println(“main”…

MySQL单表千万级数据查询优化大家怎么说(评论有亮点)

题图来自APOD 上次写了一篇MySQL优化实战的文章“MySQL千万级数据从190秒优化到1秒全过程”。 这篇文章主要还是在实战MySQL优化&#xff0c;所以从造数据到查询SQL优化SQL都没有业务或者其它依赖&#xff0c;优化的技巧也不涉及软件架构就是纯SQL优化。 由于笔者经验有限和…

mysql:部署MySQL 8.0 环境

mysql网址&#xff1a;MySQL 点击 MySQL Community Server 选择合适的版本 选择8.0版本 下载完成&#xff0c;点击mysql-installer-community-8.0.26.0.msi文件&#xff0c;打开安装向导。 选择自定义安装类型 打开“Select Products” 窗口&#xff0c;可以定制需要安装的产…

MySQL学习(8):约束

1.什么是约束 约束是作用于表中字段上的规则&#xff0c;以限制表中数据&#xff0c;保证数据的正确性、有效性、完整性 约束分为以下几种&#xff1a; not null非空约束限制该字段的数据不能为nullunique唯一约束保证该字段的所有数据都是唯一、不重复的primary key主键约束…

Oracle数据库中RETURNING子句

RETURNING子句允许您检索插入、删除或更新所修改的列&#xff08;以及基于列的表达式&#xff09;的值。如果不使用RETURNING&#xff0c;则必须在DML语句完成后运行SELECT语句&#xff0c;才能获得更改列的值。因此&#xff0c;RETURNING有助于避免再次往返数据库&#xff0c;…

EtherCAT通讯介绍

一、EtherCAT简介 EtherCAT&#xff08;Ethernet for Control Automation Technology&#xff09;是一种实时以太网技术&#xff0c;是由德国公司Beckhoff Automation在2003年首次推出的。它是一种开放的工业以太网标准&#xff0c;被设计用于满足工业自动化应用中的高性能和低…

【JVM排查问题】JProfiler性能分析工具连接远程服务器Docker容器中的Java服务

1、下载JProfiler https://www.ej-technologies.com/download/jprofiler/version_13 下载Windows版本以及Linux版本 Windows用于可视化、Linux用于在Docker容器中启动 2、将Linux版本的JProfiler上传到Docker容器中&#xff0c;宿主机cp命令到容器中 docker cp /home/data/s…

项目管理实用表格与应用【项目文件资料分享】

项目管理基础知识 项目管理可分为五大过程组&#xff08;启动、规划、执行、监控、收尾&#xff09;十大知识领域&#xff0c;其中包含49个子过程 项目十大知识领域分为&#xff1a;项目整合管理、项目范围管理、项目进度管理、项目成本管理、项目质量管理、项目资源管理、项目…

Nginx系列(二)---Mac上的快速使用

一、安装 前置软件&#xff1a;Homebrew 安装方法&#xff1a;终端输入/bin/bash -c "$(curl -fsSL <https://cdn.jsdelivr.net/gh/ineo6/homebrew-install/install.sh>)"更新&#xff1a; brew update 设置中科大镜像源&#xff1a;git -C "$(brew --r…

蓝牙模块的使用01,OOOLMF蓝牙模块HC05调试使用01AT设置从机,手机用软件对接

参考资料 https://blog.csdn.net/xia3976/article/details/122199162 1、实验目的 验证蓝牙模块是不是好的&#xff0c;能不能AT指令改变查询配置&#xff1b; 验证设置从机模式&#xff0c;成功之后&#xff0c;用手机现成的蓝牙软件&#xff08;实验室大群里面有&#xff09…

springboot 篮球馆管理系统-计算机毕业设计源码21945

目 录 摘要 1 绪论 1.1选题背景 1.2研究意义 1.3论文结构与章节安排 2 篮球馆管理系统系统分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性分析 2.1.3 法律可行性分析 2.2 系统功能分析 2.2.1 功能性分析 2.2.2 非功能性分析 2.3 系统用例分析 2.4 …

程序员的职业发展有几个选择?程序员转行的困惑与方向!

面对着日新月异的代码和语言&#xff0c;你是否感到了力不从心&#xff1f;稍有懈怠&#xff0c;就跟不上岗位需要了&#xff1f;身体渐渐的发福&#xff0c;熬夜写代码开始扛不住了吗&#xff1f; 对于老板来说&#xff0c;永远都存在更年轻、更便宜的选择。老实说&#xff0c…

高校搭建AIGC新媒体实验室,创新新闻教育教学模式

高校作为人才培养的重要阵地&#xff0c;必须紧跟时代步伐&#xff0c;不断创新教育教学模式&#xff0c;提升跨界融合育人水平&#xff0c;通过AIGC新媒体实验室探索创新人才培养模式。AIGC新媒体实验室不仅能够高效赋能高校宣传媒体矩阵&#xff0c;也可以助力教学实践与AIGC…

KUKA机器人中断编程3—暂停功能的编程

在KUKA机器人的使用过程中&#xff0c;对于调试一个项目&#xff0c;当遇到特殊情况时需要暂停机器人&#xff0c;等异常情况处理完成后再继续机器人的程序运行。wait for指令是等待一个输入信号指令&#xff0c;没有输入信号&#xff0c;机器人一直等待。在一定程度上程序也不…

vue3中使用Antv G6渲染树形结构并支持节点增删改

写在前面 在一些管理系统中&#xff0c;会对组织架构、级联数据等做一些管理&#xff0c;你会怎么实现呢&#xff1f;在经过调研很多插件之后决定使用 Antv G6 实现&#xff0c;文档也比较清晰&#xff0c;看看怎么实现吧&#xff0c;先来看看效果图。点击在线体验 实现的功能…