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

news2024/10/6 12:32:43

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);
            }
        });
    }


}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【ubuntu自启shell脚本】——在ubuntu中如何使用系统自带的启动应用程序设置开机自启自己的本地shell脚本

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、设置开机自启shell脚本1.使用 gnome-session-properties2.测试的shell例程代码 总结 前言 在Ubuntu系统中设置开机自启脚本是一种重要的自动化方法。开机自…

基于docker轻松部署selenium grid环境

做web自动化的同学都知道selenium grid非常好用&#xff0c;但是环境配置特别麻烦&#xff0c;很多人都躺在了环境搭建。那么有没有更简单的方式呢&#xff0c;答案是肯定的&#xff0c;今天我们就用docker来完成它&#xff0c;希望对大家有帮助。 一、环境准备 准备一台 Linu…

《安全大模型技术与市场研究报告》发布,海云安榜上有名

近日&#xff0c;网络安全产业研究机构“数说安全”发布2024《安全大模型技术与市场研究报告》&#xff08;以下简称“报告”&#xff09;。 海云安凭借在开发安全领域的优秀业务能力以及在大模型相关技术研究方面的成就得到了认可&#xff0c;入选“安全开发大模型推荐供应商”…

实现点击Button,改变背景颜色(多个按钮互斥显示)

一 功能描述 在界面中&#xff0c;有一组button&#xff0c;现在需要实现下面功能&#xff1a;点击其中一个&#xff0c;改变被点击button的背景颜色。当点击下一个之后&#xff0c;之前点击过的按钮背景颜色还原&#xff0c;当前被点击的button背景色又被改变。效果如下图&…

PowerDsigner的简单使用

目录 1.PowerDesinger 2.PD与navicat的区别&#xff1a; 3.使用 1.PowerDesinger 在实际开发中&#xff0c;数据库的设计会使用专业的建模工具——PowerDesinger &#xff08;安装及其破解大家搜选相关CSDN博客吧&#xff09; 2.PD与navicat的区别&#xff1a; navicat是…

Zabbix企业级监控系统

——监控可以帮助我们做什么&#xff1f; 作为一个运维&#xff0c;需要会使用监控系统查看服务器系统性能、应用服务状态和网站流量指标等&#xff0c;利用监控系统的数据去了解网站上线发布的结果和健康状态。 利用一个优秀的监控软件&#xff0c;我们可以: 通过一个友好的…

Kubernetes云原生存储解决方案openebs部署实践-4.0.1版本(helm部署)

Kubernetes云原生存储解决方案openebs部署实践-4.0.1版本&#xff08;helm部署&#xff09; 简介 OpenEBS 是一种开源云原生存储解决方案。OpenEBS 可以将 Kubernetes 工作节点可用的任何存储转化为本地或复制的 Kubernetes 持久卷。OpenEBS 帮助应用和平台团队轻松地部署需要…

FlinkCDC-3.1.1 DataStream Source

问题&#xff1a; Caused by: java.lang.ClassNotFoundException: org.apache.flink.table.catalog.ObjectPath 解决&#xff1a; 在poml文件中&#xff0c;导入的flink-table依赖把“ <scope>”去掉 <properties><maven.compiler.source>8</maven.compi…

ffmpeg将多个yuv文件编码为MP4视频文件

一、编码方案 在视频录制时&#xff0c;每一帧保存为一个yuv文件&#xff0c;便于纠错和修改。在编码为MP4文件时&#xff0c;我的方案是将所有yuv文件先转码为单个MP4文件&#xff0c;然后使用ffmpeg的concat功能拼接为完整的视频。 二、shell脚本 #!/bin/bash# 检查参数数量…

自己训练 PaddleOCR

打标工具 https://github.com/Evezerest/PPOCRLabel 感谢这位热心网友提供的标注工具&#xff0c;操作非常的方便 只是这个工具有个小坑get_rotate_crop_image&#xff08;&#xff09; 我的标注数据导出时&#xff0c;很多数据变成倒的 hmmmm, 你管我~ if dst_img_height …

Lesson 47 A cup of coffee

Lesson 47 A cup of coffee 词汇 like v. 喜欢&#xff0c;想要 用法&#xff1a;like 物品 / 人 喜欢……    like 动词ing 喜欢做……&#xff08;习惯性&#xff09;    like to 动词原形 喜欢做……&#xff08;一次性&#xff09; 例句&#xff1a;我喜欢小狗…

【UE5.1】Chaos物理系统基础——03 炸开几何体集

目录 步骤 一、通过径向向量将几何体集炸开 二、优化炸开效果——让破裂的碎块自然下落 三、优化炸开效果——让碎块旋转起来 四、优化炸开效果——让碎块旋转的越来越慢 步骤 一、通过径向向量将几何体集炸开 1. 打开上一篇中&#xff08;【UE5.1】Chaos物理系统基础—…

第3章.中央服务器的物联网模式--规则引擎

规则引擎 规则引擎本质上是物联网事件和需要与这些事件相关联的动作之间的映射。在物联网环境中&#xff0c;事件通常使用传感器生成&#xff0c;所需的动作由执行器采取。本书中用于该图案的符号如下图所示&#xff1a; 图3.6–“规则引擎”模式的符号 一个有趣的类比是将规则…

Vue2前端实现数据可视化大屏全局自适应 Vue实现所有页面自适应 Vue实现自适应所有屏幕

Vue自适应所有屏幕大小,目前页面自适应,尤其是数据可视化大屏的自适应更是案例很多 今天就记录一下使用Vue全局自适应各种屏幕大小的功能 在Vue.js中创建一个数据大屏,并使其能够自适应不同屏幕大小,通常涉及到布局的响应式设计、CSS媒体查询、以及利用Vue的事件系统来处理…

stm8玩耍日记1

写在前面&#xff0c;如题所示&#xff0c;这是一个stm8L051F3的玩耍记录。 环境使用的是IAR for stm8&#xff0c;使用stlink v2作为调试下载器&#xff0c;跟着st中文论坛的一个大佬的教程学习的。 整体配置下来&#xff0c;点亮了led&#xff0c;感觉和stm32的开发差不多&…

电脑鼠标一直转圈圈怎么处理?对症下药,分享6种方法

在使用电脑的过程中&#xff0c;鼠标一直转圈圈是一个常见且令人困扰的问题。这种情况通常意味着系统正在处理某些任务&#xff0c;但如果持续时间过长&#xff0c;可能表明系统存在性能问题或错误。本文将详细探讨鼠标一直转圈圈的常见原因及其解决方法。 摘要 电脑鼠标一直转…

解决Python爬虫开发中的数据输出问题:确保正确生成CSV文件

引言 在大数据时代&#xff0c;爬虫技术成为获取和分析网络数据的重要工具。然而&#xff0c;许多开发者在使用Python编写爬虫时&#xff0c;常常遇到数据输出问题&#xff0c;尤其是在生成CSV文件时出错。本文将详细介绍如何解决这些问题&#xff0c;并提供使用代理IP和多线程…

2024.7.4作业

1.梳理笔记(原创) 2. 终端输入一个日期&#xff0c;判断是这一年的第几天 scanf("%d-%d-%d",&y,&m,&d); 闰年2月29天&#xff0c;平年2月28天 #include <stdio.h> int main(int argc,const char *argv[]) { int y0,m0,d0,sum0,i0; …

打卡第2天----数组双指针,滑动窗口

今天是参与训练营第二天&#xff0c;这几道题我都看懂了&#xff0c;自己也能写出来了&#xff0c;实现思路很重要&#xff0c;万事开头难&#xff0c;希望我可以坚持下去。希望最后的结果是量变带来质变。 一、理解双指针思想 leetcode编号&#xff1a;977 不止是在卡尔这里…