手机实现真随机数生成器

上次讲了通过传感器实现真随机算法,今天讲一下另外的方法:借助手机和平板硬件生成物理随机数,并且生成的数字无法预测,无法重复。方法是利用系统摄像头获取视频数据,再转换成随机数种子,同理也可以用麦克风进行录制音频数据作为种子。

package com.zwxuf.randomgenerator;

import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.graphics.drawable.ColorDrawable;
import android.hardware.Camera;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.media.MediaRecorder;
import android.os.Handler;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.TextureView;
import android.view.WindowManager;

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import androidx.annotation.NonNull;

public class RandomGenerator implements TextureView.SurfaceTextureListener {

    private static final String TAG = "RandomGenerator";

    private Context mContext;
    private CameraDevice mCameraDevice;
    private ImageReader mImageReader;
    private CameraCaptureSession mCameraCaptureSession;
    private OnGenerateListener onGenerateListener;
    private boolean mStarting;
    private boolean mStopped;
    private CameraManager mCameraManager;
    private TextureView mTextureView;
    private Size mPreviewSize;
    private boolean mCanceled;

    public RandomGenerator(Context mContext) {
        this.mContext = mContext;
        mCameraManager = (CameraManager) mContext.getSystemService(Service.CAMERA_SERVICE);
    }


    public void start() {
        mTextureView.setSurfaceTextureListener(this);
        openCamera(mTextureView.getWidth(), mTextureView.getHeight());
    }

    @SuppressLint("MissingPermission")
    private void openCamera(int width, int height) {
        if (isStarting()) return;
        mStarting = true;
        mStopped = false;
        mCanceled = false;
        try {
            String cameraId = getMainCameraId(width, height);
            Log.i(TAG, "cameraId=" + cameraId);
            mCameraManager.openCamera(cameraId, new CameraDevice.StateCallback() {
                @Override
                public void onOpened(@NonNull CameraDevice camera) {
                    mCameraDevice = camera;
                    startPreview();
                }

                @Override
                public void onDisconnected(@NonNull CameraDevice camera) {
                    if (!mStopped) {
                        assertTask("相机断开");
                    }
                }

                @Override
                public void onError(@NonNull CameraDevice camera, int error) {
                    assertTask("打开相机失败,错误代码" + error);
                }
            }, null);
        } catch (Exception e) {
            Log.e(TAG, e.toString());
            assertTask("打开相机失败:" + e.toString());
        }
    }

    private String getMainCameraId(int width, int height) {
        String id = null;
        try {
            for (String cameraId : mCameraManager.getCameraIdList()) {
                CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(cameraId);
                //默认打开后置摄像头
                if (characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT)
                    continue;
                StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                mPreviewSize = getOptimalSize(map.getOutputSizes(SurfaceTexture.class), width, height);
                id = cameraId;
                break;
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
        return id == null ? "0" : id;
    }

    /**
     * 从摄像头支持的预览Size中选择大于并且最接近width和height的size
     *
     * @param sizeMap
     * @param width
     * @param height
     * @return
     */
    private Size getOptimalSize(Size[] sizeMap, int width, int height) {
        List<Size> sizeList = new ArrayList<>();
        for (Size option : sizeMap) {
            //Dbg.print("系统支持的尺寸大小==width:" + option.getWidth() + " height:" + option.getHeight());
            if (width > height) {
                if (option.getWidth() > width && option.getHeight() > height) {
                    sizeList.add(option);
                }
            } else {
                if (option.getWidth() > height && option.getHeight() > width) {
                    sizeList.add(option);
                }
            }
        }
        if (sizeList.size() > 0) {
            return Collections.min(sizeList, new Comparator<Size>() {
                @Override
                public int compare(Size lhs, Size rhs) {
                    return Long.signum(lhs.getWidth() * lhs.getHeight() - rhs.getWidth() * rhs.getHeight());
                }
            });
        }
        if (sizeList.size() > 0)
            return sizeList.get(0);
        return sizeMap[0];
    }


    private void startPreview() {
        try {
            SurfaceTexture mSurfaceTexture = mTextureView.getSurfaceTexture();
            mSurfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
            Surface mSurface = new Surface(mSurfaceTexture);
            final CaptureRequest.Builder builder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            builder.addTarget(mSurface);
            mCameraDevice.createCaptureSession(Collections.singletonList(mSurface), new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(@NonNull CameraCaptureSession session) {
                    mCameraCaptureSession = session;
                    try {
                        session.setRepeatingRequest(builder.build(), mCaptureCallback, null); // 开始连续捕获图片
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                        assertTask(null);
                    }
                }

                @Override
                public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                    assertTask(null);
                }
            }, null);
        } catch (Exception e) {
            e.printStackTrace();
            assertTask(null);
        }
    }

    private void release() {
        mStopped = true;
        if (mCameraCaptureSession != null) {
            mCameraCaptureSession.close();
            mCameraCaptureSession = null;
        }
//        try {
//            if (mImageReader != null) {
//                mImageReader.close();
//                mImageReader = null;
//            }
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
        try {
            if (mCameraDevice != null) {
                mCameraDevice.close();
                mCameraDevice = null;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        mStarting = false;
    }

    private void assertTask(String msg) {
        release();
        if (onGenerateListener != null) {
            onGenerateListener.onError(msg);
        }
    }

    public void stop() {
        if (!isStarting()) return;
        release();
        if (mCanceled) return;
        if (onGenerateListener == null) return;
        Bitmap bitmap = mTextureView.getBitmap();
        if (bitmap != null) {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
            byte[] data = out.toByteArray();
            long seed = makeRandomSeed(data);
            mTextureView.invalidate();
            if (onGenerateListener != null) {
                onGenerateListener.onSuccess(seed);
            }
        }
    }

    /**
     * 制作随机数种子
     * @param source
     * @return
     */
    private long makeRandomSeed(byte[] source) {
        if (source == null || source.length == 0) {
            return 0;
        }
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            digest.update(source);
            byte[] hashBytes = digest.digest();
            BigInteger bi = new BigInteger(1, hashBytes);
            return bi.remainder(BigInteger.valueOf(Long.MAX_VALUE)).longValue(); //取余,也可以取中间8个字节
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    private CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() {

    };

    public void setOnGenerateListener(OnGenerateListener onGenerateListener) {
        this.onGenerateListener = onGenerateListener;
    }

    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        Log.i(TAG, "onSurfaceTextureAvailable");
        openCamera(width, height);
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        Log.i(TAG, "onSurfaceTextureDestroyed");
        assertTask(null);
        return false;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {

    }

    public void cancel() {
        mCanceled = true;
        release();
        if (onGenerateListener != null) {
            onGenerateListener.onCancel();
        }
    }

    public boolean isCanceled() {
        return mCanceled;
    }


    public interface OnGenerateListener {
        void onSuccess(long seed);

        void onError(String msg);

        void onCancel();
    }

    public boolean isStarting() {
        return mStarting;
    }

    public void setTextureView(TextureView mTextureView) {
        this.mTextureView = mTextureView;
    }
}
package com.zwxuf.randomgenerator;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.annotation.SuppressLint;
import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceView;
import android.view.TextureView;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

public class MainActivity extends AppCompatActivity implements View.OnTouchListener, RandomGenerator.OnGenerateListener {

    private static final String TAG = "RandomGenerator";

    private Button bn_generate;
    private RandomGenerator mGenerator;
    private TextureView mTextureView;
    private TextView tv_content;

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


        mTextureView = findViewById(R.id.mTextureView);
        bn_generate = findViewById(R.id.bn_generate);
        tv_content = findViewById(R.id.tv_content);

        mGenerator = new RandomGenerator(this);
        mGenerator.setOnGenerateListener(this);
        mGenerator.setTextureView(mTextureView);

        bn_generate.setOnTouchListener(this);
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
                    mTextureView.setVisibility(View.VISIBLE);
                    tv_content.setVisibility(View.INVISIBLE);
                    mGenerator.start();
                } else {
                    ActivityCompat.requestPermissions(this, new String[]{
                            Manifest.permission.CAMERA
                    }, 1000);
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (mGenerator.isCanceled()) return false;
                int x = (int) event.getX();
                int y = (int) event.getY();
                Rect localRect = new Rect();
                bn_generate.getLocalVisibleRect(localRect);
                if (!localRect.contains(x, y)) {
                    mGenerator.cancel();
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                        bn_generate.releasePointerCapture();
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mGenerator.stop();
                break;
        }
        return false;
    }

    @Override
    public void onSuccess(long seed) {
        Log.i(TAG, "seed=" + seed);
        Toast.makeText(this, "" + seed, Toast.LENGTH_SHORT).show();

        mTextureView.setVisibility(View.INVISIBLE);
        tv_content.setVisibility(View.VISIBLE);
        //使用Random正常生成随机数
        Random random = new Random(seed);
        //例:生成五注双色球
        tv_content.setText(null);
        for (int i = 0; i < 5; i++) {
            String numbers = generateUnionLotto(random);
            tv_content.append(numbers);
            tv_content.append("\n");
        }
    }

    private String generateUnionLotto(Random random) {
        LinkedList<Integer> numbers = new LinkedList<>();
        for (int i = 0; i < 33; i++) {
            numbers.add(i + 1);
        }
        StringBuilder sb = new StringBuilder();
        int[] reds = new int[6];
        for (int i = 0; i < 6; i++) {
            int index = random.nextInt(numbers.size());
            reds[i] = numbers.get(index);
            numbers.remove(index);
        }
        int[] blues = new int[1];
        for (int i = 0; i < blues.length; i++) {
            blues[i] = random.nextInt(16) + 1;
        }
        Arrays.sort(reds);
        Arrays.sort(blues);
        for (int red : reds) {
            sb.append(String.format("%02d", red)).append(" ");
        }
        sb.append(" + ");
        for (int blue : blues) {
            sb.append(String.format("%02d", blue));
        }
        return sb.toString();
    }


    @Override
    public void onError(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onCancel() {
        mTextureView.setVisibility(View.INVISIBLE);
        Toast.makeText(this, "取消", Toast.LENGTH_SHORT).show();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值