1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.example.android.vdmdemo.client; 18 19 import android.content.Context; 20 import android.hardware.Sensor; 21 import android.hardware.SensorAdditionalInfo; 22 import android.hardware.SensorEvent; 23 import android.hardware.SensorEventCallback; 24 import android.hardware.SensorManager; 25 import android.os.Handler; 26 import android.os.HandlerThread; 27 28 import com.example.android.vdmdemo.common.RemoteEventProto.RemoteEvent; 29 import com.example.android.vdmdemo.common.RemoteEventProto.RemoteSensorEvent; 30 import com.example.android.vdmdemo.common.RemoteEventProto.SensorCapabilities; 31 import com.example.android.vdmdemo.common.RemoteEventProto.SensorConfiguration; 32 import com.example.android.vdmdemo.common.RemoteIo; 33 import com.google.common.primitives.Floats; 34 35 import dagger.hilt.android.qualifiers.ApplicationContext; 36 import dagger.hilt.android.scopes.ActivityScoped; 37 38 import java.util.List; 39 import java.util.Objects; 40 import java.util.function.Consumer; 41 import java.util.stream.Collectors; 42 import java.util.stream.Stream; 43 44 import javax.inject.Inject; 45 46 @ActivityScoped 47 final class VirtualSensorController implements AutoCloseable { 48 49 private final RemoteIo mRemoteIo; 50 private final Consumer<RemoteEvent> mRemoteEventConsumer = this::processRemoteEvent; 51 private final SensorManager mSensorManager; 52 private final HandlerThread mListenerThread; 53 private final Handler mHandler; 54 55 private final SensorEventCallback mSensorEventCallback = new SensorEventCallback() { 56 @Override 57 public void onSensorChanged(SensorEvent event) { 58 mRemoteIo.sendMessage(RemoteEvent.newBuilder() 59 .setSensorEvent(RemoteSensorEvent.newBuilder() 60 .setSensorType(event.sensor.getType()) 61 .setAdditionalInfoType(-1) 62 .addAllValues(Floats.asList(event.values))) 63 .build()); 64 } 65 66 @Override 67 public void onSensorAdditionalInfo(SensorAdditionalInfo info) { 68 if (info.type != SensorAdditionalInfo.TYPE_INTERNAL_TEMPERATURE) { 69 return; 70 } 71 mRemoteIo.sendMessage(RemoteEvent.newBuilder() 72 .setSensorEvent(RemoteSensorEvent.newBuilder() 73 .setSensorType(info.sensor.getType()) 74 .setAdditionalInfoType(info.type) 75 .addAllValues(List.of(info.floatValues[0]))) 76 .build()); 77 } 78 }; 79 80 @Inject VirtualSensorController(@pplicationContext Context context, RemoteIo remoteIo)81 VirtualSensorController(@ApplicationContext Context context, RemoteIo remoteIo) { 82 mSensorManager = context.getSystemService(SensorManager.class); 83 mRemoteIo = remoteIo; 84 85 mListenerThread = new HandlerThread("VirtualSensorListener"); 86 mListenerThread.start(); 87 mHandler = new Handler(mListenerThread.getLooper()); 88 89 remoteIo.addMessageConsumer(mRemoteEventConsumer); 90 } 91 92 @Override close()93 public void close() { 94 mSensorManager.unregisterListener(mSensorEventCallback); 95 mListenerThread.quitSafely(); 96 mRemoteIo.removeMessageConsumer(mRemoteEventConsumer); 97 } 98 getSensorCapabilities()99 public List<SensorCapabilities> getSensorCapabilities() { 100 // For demo purposes we only need a single accelerometer and proximity sensor. 101 return Stream.of( 102 mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), 103 mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)) 104 .filter(Objects::nonNull) 105 .map(VirtualSensorController::createSensorCapabilitiesFromSensor) 106 .collect(Collectors.toList()); 107 } 108 createSensorCapabilitiesFromSensor(Sensor sensor)109 private static SensorCapabilities createSensorCapabilitiesFromSensor(Sensor sensor) { 110 return SensorCapabilities.newBuilder() 111 .setType(sensor.getType()) 112 .setName(sensor.getName()) 113 .setVendor(sensor.getVendor()) 114 .setMaxRange(sensor.getMaximumRange()) 115 .setResolution(sensor.getResolution()) 116 .setPower(sensor.getPower()) 117 .setMinDelayUs(sensor.getMinDelay()) 118 .setMaxDelayUs(sensor.getMaxDelay()) 119 .setIsWakeUpSensor(sensor.isWakeUpSensor()) 120 .setReportingMode(sensor.getReportingMode()) 121 .setIsAdditionalInfoSupported(sensor.isAdditionalInfoSupported()) 122 .build(); 123 } 124 processRemoteEvent(RemoteEvent remoteEvent)125 private void processRemoteEvent(RemoteEvent remoteEvent) { 126 if (!remoteEvent.hasSensorConfiguration()) { 127 return; 128 } 129 SensorConfiguration config = remoteEvent.getSensorConfiguration(); 130 Sensor sensor = mSensorManager.getDefaultSensor(config.getSensorType()); 131 if (sensor == null) { 132 return; 133 } 134 if (config.getEnabled()) { 135 mSensorManager.registerListener( 136 mSensorEventCallback, 137 sensor, 138 config.getSamplingPeriodUs(), 139 config.getBatchReportingLatencyUs(), 140 mHandler); 141 } else { 142 mSensorManager.unregisterListener(mSensorEventCallback, sensor); 143 } 144 } 145 } 146