手机实现真随机数生成器
上次讲了通过传感器实现真随机算法,今天讲一下另外的方法:借助手机和平板硬件生成物理随机数,并且生成的数字无法预测,无法重复。方法是利用系统摄像头获取视频数据,再转换成随机数种子,同理也可以用麦克风进行录制音频数据作为种子。
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() {@Overridepublic void onOpened(@NonNull CameraDevice camera) {mCameraDevice = camera;startPreview();}@Overridepublic void onDisconnected(@NonNull CameraDevice camera) {if (!mStopped) {assertTask("相机断开");}}@Overridepublic 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>() {@Overridepublic 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() {@Overridepublic void onConfigured(@NonNull CameraCaptureSession session) {mCameraCaptureSession = session;try {session.setRepeatingRequest(builder.build(), mCaptureCallback, null); // 开始连续捕获图片} catch (CameraAccessException e) {e.printStackTrace();assertTask(null);}}@Overridepublic 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;}@Overridepublic void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {Log.i(TAG, "onSurfaceTextureAvailable");openCamera(width, height);}@Overridepublic void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {}@Overridepublic boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {Log.i(TAG, "onSurfaceTextureDestroyed");assertTask(null);return false;}@Overridepublic 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;@Overrideprotected 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);}@Overridepublic 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;}@Overridepublic 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();}@Overridepublic void onError(String msg) {Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();}@Overridepublic void onCancel() {mTextureView.setVisibility(View.INVISIBLE);Toast.makeText(this, "取消", Toast.LENGTH_SHORT).show();}
}