Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 79101e96 authored by Vladimir Komsiyski's avatar Vladimir Komsiyski Committed by Android (Google) Code Review
Browse files

Merge changes from topic "virtual-sensors"

* changes:
  Adjust the behavior of SensorManager for virtual device sensors.
  Virtual sensors lifecycle management.
  Virtual sensor API.
parents 1b8935ae c0acd6cd
Loading
Loading
Loading
Loading
+47 −0
Original line number Diff line number Diff line
@@ -2964,6 +2964,7 @@ package android.companion.virtual {
    method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
    method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
    method public int getDeviceId();
    method @Nullable public android.companion.virtual.sensor.VirtualSensor getVirtualSensor(int, @NonNull String);
    method public void launchPendingIntent(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
    method public void removeActivityListener(@NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener);
    method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setShowPointerIcon(boolean);
@@ -2981,6 +2982,7 @@ package android.companion.virtual {
    method public int getLockState();
    method @Nullable public String getName();
    method @NonNull public java.util.Set<android.os.UserHandle> getUsersWithMatchingAccounts();
    method @NonNull public java.util.List<android.companion.virtual.sensor.VirtualSensorConfig> getVirtualSensorConfigs();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field public static final int ACTIVITY_POLICY_DEFAULT_ALLOWED = 0; // 0x0
    field public static final int ACTIVITY_POLICY_DEFAULT_BLOCKED = 1; // 0x1
@@ -2997,6 +2999,7 @@ package android.companion.virtual {
  public static final class VirtualDeviceParams.Builder {
    ctor public VirtualDeviceParams.Builder();
    method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder addDevicePolicy(int, int);
    method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder addVirtualSensorConfig(@NonNull android.companion.virtual.sensor.VirtualSensorConfig);
    method @NonNull public android.companion.virtual.VirtualDeviceParams build();
    method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedActivities(@NonNull java.util.Set<android.content.ComponentName>);
    method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedCrossTaskNavigations(@NonNull java.util.Set<android.content.ComponentName>);
@@ -3054,6 +3057,50 @@ package android.companion.virtual.audio {
}
package android.companion.virtual.sensor {
  public class VirtualSensor {
    method @NonNull public String getName();
    method public int getType();
    method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendSensorEvent(@NonNull android.companion.virtual.sensor.VirtualSensorEvent);
  }
  public static interface VirtualSensor.SensorStateChangeCallback {
    method public void onStateChanged(boolean, @NonNull java.time.Duration, @NonNull java.time.Duration);
  }
  public final class VirtualSensorConfig implements android.os.Parcelable {
    method public int describeContents();
    method @NonNull public String getName();
    method public int getType();
    method @Nullable public String getVendor();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.sensor.VirtualSensorConfig> CREATOR;
  }
  public static final class VirtualSensorConfig.Builder {
    ctor public VirtualSensorConfig.Builder(int, @NonNull String);
    method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig build();
    method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setStateChangeCallback(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.sensor.VirtualSensor.SensorStateChangeCallback);
    method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setVendor(@Nullable String);
  }
  public final class VirtualSensorEvent implements android.os.Parcelable {
    method public int describeContents();
    method public long getTimestampNanos();
    method @NonNull public float[] getValues();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.sensor.VirtualSensorEvent> CREATOR;
  }
  public static final class VirtualSensorEvent.Builder {
    ctor public VirtualSensorEvent.Builder(@NonNull float[]);
    method @NonNull public android.companion.virtual.sensor.VirtualSensorEvent build();
    method @NonNull public android.companion.virtual.sensor.VirtualSensorEvent.Builder setTimestampNanos(long);
  }
}
package android.content {
  public class ApexEnvironment {
+21 −0
Original line number Diff line number Diff line
@@ -19,6 +19,9 @@ package android.companion.virtual;
import android.app.PendingIntent;
import android.companion.virtual.audio.IAudioConfigChangedCallback;
import android.companion.virtual.audio.IAudioRoutingCallback;
import android.companion.virtual.sensor.IVirtualSensorStateChangeCallback;
import android.companion.virtual.sensor.VirtualSensorConfig;
import android.companion.virtual.sensor.VirtualSensorEvent;
import android.graphics.Point;
import android.graphics.PointF;
import android.hardware.input.VirtualKeyEvent;
@@ -96,6 +99,24 @@ interface IVirtualDevice {
    boolean sendScrollEvent(IBinder token, in VirtualMouseScrollEvent event);
    boolean sendTouchEvent(IBinder token, in VirtualTouchEvent event);

    /**
     * Creates a virtual sensor, capable of injecting sensor events into the system.
     */
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)")
    void createVirtualSensor(IBinder tokenm, in VirtualSensorConfig config);

    /**
     * Removes the sensor corresponding to the given token from the system.
     */
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)")
    void unregisterSensor(IBinder token);

    /**
     * Sends an event to the virtual sensor corresponding to the given token.
     */
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)")
    boolean sendSensorEvent(IBinder token, in VirtualSensorEvent event);

    /**
     * Launches a pending intent on the given display that is owned by this virtual device.
     */
+71 −0
Original line number Diff line number Diff line
@@ -22,12 +22,15 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.app.PendingIntent;
import android.companion.AssociationInfo;
import android.companion.virtual.audio.VirtualAudioDevice;
import android.companion.virtual.audio.VirtualAudioDevice.AudioConfigurationChangeCallback;
import android.companion.virtual.sensor.VirtualSensor;
import android.companion.virtual.sensor.VirtualSensorConfig;
import android.content.ComponentName;
import android.content.Context;
import android.graphics.Point;
@@ -58,6 +61,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.IntConsumer;

@@ -89,6 +93,26 @@ public final class VirtualDeviceManager {
     */
    public static final int INVALID_DEVICE_ID = -1;

    /**
     * Broadcast Action: A Virtual Device was removed.
     *
     * <p class="note">This is a protected intent that can only be sent by the system.</p>
     *
     * @hide
     */
    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String ACTION_VIRTUAL_DEVICE_REMOVED =
            "android.companion.virtual.action.VIRTUAL_DEVICE_REMOVED";

    /**
     * Int intent extra to be used with {@link #ACTION_VIRTUAL_DEVICE_REMOVED}.
     * Contains the identifier of the virtual device, which was removed.
     *
     * @hide
     */
    public static final String EXTRA_VIRTUAL_DEVICE_ID =
            "android.companion.virtual.extra.VIRTUAL_DEVICE_ID";

    /** @hide */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(
@@ -251,7 +275,10 @@ public final class VirtualDeviceManager {
                };
        @Nullable
        private VirtualAudioDevice mVirtualAudioDevice;
        @NonNull
        private List<VirtualSensor> mVirtualSensors = new ArrayList<>();

        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
        private VirtualDevice(
                IVirtualDeviceManager service,
                Context context,
@@ -265,6 +292,10 @@ public final class VirtualDeviceManager {
                    associationId,
                    params,
                    mActivityListenerBinder);
            final List<VirtualSensorConfig> virtualSensorConfigs = params.getVirtualSensorConfigs();
            for (int i = 0; i < virtualSensorConfigs.size(); ++i) {
                mVirtualSensors.add(createVirtualSensor(virtualSensorConfigs.get(i)));
            }
        }

        /**
@@ -278,6 +309,23 @@ public final class VirtualDeviceManager {
            }
        }

        /**
         * Returns this device's sensor with the given type and name, if any.
         *
         * @see VirtualDeviceParams.Builder#addVirtualSensorConfig
         *
         * @param type The type of the sensor.
         * @param name The name of the sensor.
         * @return The matching sensor if found, {@code null} otherwise.
         */
        @Nullable
        public VirtualSensor getVirtualSensor(int type, @NonNull String name) {
            return mVirtualSensors.stream()
                    .filter(sensor -> sensor.getType() == type && sensor.getName().equals(name))
                    .findAny()
                    .orElse(null);
        }

        /**
         * Launches a given pending intent on the give display ID.
         *
@@ -438,6 +486,7 @@ public final class VirtualDeviceManager {
        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
        public void close() {
            try {
                // This also takes care of unregistering all virtual sensors.
                mVirtualDevice.close();
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
@@ -622,6 +671,28 @@ public final class VirtualDeviceManager {
            }
        }

        /**
         * Creates a virtual sensor, capable of injecting sensor events into the system. Only for
         * internal use, since device sensors must remain valid for the entire lifetime of the
         * device.
         *
         * @param config The configuration of the sensor.
         * @hide
         */
        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
        @NonNull
        public VirtualSensor createVirtualSensor(@NonNull VirtualSensorConfig config) {
            Objects.requireNonNull(config);
            try {
                final IBinder token = new Binder(
                        "android.hardware.sensor.VirtualSensor:" + config.getName());
                mVirtualDevice.createVirtualSensor(token, config);
                return new VirtualSensor(config.getType(), config.getName(), mVirtualDevice, token);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }

        /**
         * Adds an activity listener to listen for events such as top activity change or virtual
         * display task stack became empty.
+76 −27
Original line number Diff line number Diff line
@@ -23,20 +23,22 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.companion.virtual.sensor.VirtualSensorConfig;
import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.SparseArray;
import android.util.SparseIntArray;

import com.android.internal.util.Preconditions;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;

@@ -158,6 +160,7 @@ public final class VirtualDeviceParams implements Parcelable {
    @Nullable private final String mName;
    // Mapping of @PolicyType to @DevicePolicy
    @NonNull private final SparseIntArray mDevicePolicies;
    @NonNull private final List<VirtualSensorConfig> mVirtualSensorConfigs;

    private VirtualDeviceParams(
            @LockState int lockState,
@@ -169,24 +172,22 @@ public final class VirtualDeviceParams implements Parcelable {
            @NonNull Set<ComponentName> blockedActivities,
            @ActivityPolicy int defaultActivityPolicy,
            @Nullable String name,
            @NonNull SparseIntArray devicePolicies) {
        Preconditions.checkNotNull(usersWithMatchingAccounts);
        Preconditions.checkNotNull(allowedCrossTaskNavigations);
        Preconditions.checkNotNull(blockedCrossTaskNavigations);
        Preconditions.checkNotNull(allowedActivities);
        Preconditions.checkNotNull(blockedActivities);
        Preconditions.checkNotNull(devicePolicies);

            @NonNull SparseIntArray devicePolicies,
            @NonNull List<VirtualSensorConfig> virtualSensorConfigs) {
        mLockState = lockState;
        mUsersWithMatchingAccounts = new ArraySet<>(usersWithMatchingAccounts);
        mAllowedCrossTaskNavigations = new ArraySet<>(allowedCrossTaskNavigations);
        mBlockedCrossTaskNavigations = new ArraySet<>(blockedCrossTaskNavigations);
        mUsersWithMatchingAccounts =
                new ArraySet<>(Objects.requireNonNull(usersWithMatchingAccounts));
        mAllowedCrossTaskNavigations =
                new ArraySet<>(Objects.requireNonNull(allowedCrossTaskNavigations));
        mBlockedCrossTaskNavigations =
                new ArraySet<>(Objects.requireNonNull(blockedCrossTaskNavigations));
        mDefaultNavigationPolicy = defaultNavigationPolicy;
        mAllowedActivities = new ArraySet<>(allowedActivities);
        mBlockedActivities = new ArraySet<>(blockedActivities);
        mAllowedActivities = new ArraySet<>(Objects.requireNonNull(allowedActivities));
        mBlockedActivities = new ArraySet<>(Objects.requireNonNull(blockedActivities));
        mDefaultActivityPolicy = defaultActivityPolicy;
        mName = name;
        mDevicePolicies = devicePolicies;
        mDevicePolicies = Objects.requireNonNull(devicePolicies);
        mVirtualSensorConfigs = Objects.requireNonNull(virtualSensorConfigs);
    }

    @SuppressWarnings("unchecked")
@@ -201,6 +202,8 @@ public final class VirtualDeviceParams implements Parcelable {
        mDefaultActivityPolicy = parcel.readInt();
        mName = parcel.readString8();
        mDevicePolicies = parcel.readSparseIntArray();
        mVirtualSensorConfigs = new ArrayList<>();
        parcel.readTypedList(mVirtualSensorConfigs, VirtualSensorConfig.CREATOR);
    }

    /**
@@ -316,6 +319,15 @@ public final class VirtualDeviceParams implements Parcelable {
        return mDevicePolicies.get(policyType, DEVICE_POLICY_DEFAULT);
    }

    /**
     * Returns the configurations for all sensors that should be created for this device.
     *
     * @see Builder#addVirtualSensorConfig
     */
    public @NonNull List<VirtualSensorConfig> getVirtualSensorConfigs() {
        return mVirtualSensorConfigs;
    }

    @Override
    public int describeContents() {
        return 0;
@@ -333,6 +345,7 @@ public final class VirtualDeviceParams implements Parcelable {
        dest.writeInt(mDefaultActivityPolicy);
        dest.writeString8(mName);
        dest.writeSparseIntArray(mDevicePolicies);
        dest.writeTypedList(mVirtualSensorConfigs);
    }

    @Override
@@ -428,6 +441,7 @@ public final class VirtualDeviceParams implements Parcelable {
        private boolean mDefaultActivityPolicyConfigured = false;
        @Nullable private String mName;
        @NonNull private SparseIntArray mDevicePolicies = new SparseIntArray();
        @NonNull private List<VirtualSensorConfig> mVirtualSensorConfigs = new ArrayList<>();

        /**
         * Sets the lock state of the device. The permission {@code ADD_ALWAYS_UNLOCKED_DISPLAY}
@@ -467,8 +481,7 @@ public final class VirtualDeviceParams implements Parcelable {
        @NonNull
        public Builder setUsersWithMatchingAccounts(
                @NonNull Set<UserHandle> usersWithMatchingAccounts) {
            Preconditions.checkNotNull(usersWithMatchingAccounts);
            mUsersWithMatchingAccounts = usersWithMatchingAccounts;
            mUsersWithMatchingAccounts = Objects.requireNonNull(usersWithMatchingAccounts);
            return this;
        }

@@ -491,7 +504,6 @@ public final class VirtualDeviceParams implements Parcelable {
        @NonNull
        public Builder setAllowedCrossTaskNavigations(
                @NonNull Set<ComponentName> allowedCrossTaskNavigations) {
            Preconditions.checkNotNull(allowedCrossTaskNavigations);
            if (mDefaultNavigationPolicyConfigured
                    && mDefaultNavigationPolicy != NAVIGATION_POLICY_DEFAULT_BLOCKED) {
                throw new IllegalArgumentException(
@@ -500,7 +512,7 @@ public final class VirtualDeviceParams implements Parcelable {
            }
            mDefaultNavigationPolicy = NAVIGATION_POLICY_DEFAULT_BLOCKED;
            mDefaultNavigationPolicyConfigured = true;
            mAllowedCrossTaskNavigations = allowedCrossTaskNavigations;
            mAllowedCrossTaskNavigations = Objects.requireNonNull(allowedCrossTaskNavigations);
            return this;
        }

@@ -523,7 +535,6 @@ public final class VirtualDeviceParams implements Parcelable {
        @NonNull
        public Builder setBlockedCrossTaskNavigations(
                @NonNull Set<ComponentName> blockedCrossTaskNavigations) {
            Preconditions.checkNotNull(blockedCrossTaskNavigations);
            if (mDefaultNavigationPolicyConfigured
                     && mDefaultNavigationPolicy != NAVIGATION_POLICY_DEFAULT_ALLOWED) {
                throw new IllegalArgumentException(
@@ -532,7 +543,7 @@ public final class VirtualDeviceParams implements Parcelable {
            }
            mDefaultNavigationPolicy = NAVIGATION_POLICY_DEFAULT_ALLOWED;
            mDefaultNavigationPolicyConfigured = true;
            mBlockedCrossTaskNavigations = blockedCrossTaskNavigations;
            mBlockedCrossTaskNavigations = Objects.requireNonNull(blockedCrossTaskNavigations);
            return this;
        }

@@ -551,7 +562,6 @@ public final class VirtualDeviceParams implements Parcelable {
         */
        @NonNull
        public Builder setAllowedActivities(@NonNull Set<ComponentName> allowedActivities) {
            Preconditions.checkNotNull(allowedActivities);
            if (mDefaultActivityPolicyConfigured
                    && mDefaultActivityPolicy != ACTIVITY_POLICY_DEFAULT_BLOCKED) {
                throw new IllegalArgumentException(
@@ -559,7 +569,7 @@ public final class VirtualDeviceParams implements Parcelable {
            }
            mDefaultActivityPolicy = ACTIVITY_POLICY_DEFAULT_BLOCKED;
            mDefaultActivityPolicyConfigured = true;
            mAllowedActivities = allowedActivities;
            mAllowedActivities = Objects.requireNonNull(allowedActivities);
            return this;
        }

@@ -578,7 +588,6 @@ public final class VirtualDeviceParams implements Parcelable {
         */
        @NonNull
        public Builder setBlockedActivities(@NonNull Set<ComponentName> blockedActivities) {
            Preconditions.checkNotNull(blockedActivities);
            if (mDefaultActivityPolicyConfigured
                    && mDefaultActivityPolicy != ACTIVITY_POLICY_DEFAULT_ALLOWED) {
                throw new IllegalArgumentException(
@@ -586,7 +595,7 @@ public final class VirtualDeviceParams implements Parcelable {
            }
            mDefaultActivityPolicy = ACTIVITY_POLICY_DEFAULT_ALLOWED;
            mDefaultActivityPolicyConfigured = true;
            mBlockedActivities = blockedActivities;
            mBlockedActivities = Objects.requireNonNull(blockedActivities);
            return this;
        }

@@ -620,11 +629,50 @@ public final class VirtualDeviceParams implements Parcelable {
            return this;
        }

        /**
         * Adds a configuration for a sensor that should be created for this virtual device.
         *
         * Device sensors must remain valid for the entire lifetime of the device, hence they are
         * created together with the device itself, and removed when the device is removed.
         *
         * Requires {@link #DEVICE_POLICY_CUSTOM} to be set for {@link #POLICY_TYPE_SENSORS}.
         *
         * @see android.companion.virtual.sensor.VirtualSensor
         * @see #addDevicePolicy
         */
        @NonNull
        public Builder addVirtualSensorConfig(@NonNull VirtualSensorConfig virtualSensorConfig) {
            mVirtualSensorConfigs.add(Objects.requireNonNull(virtualSensorConfig));
            return this;
        }

        /**
         * Builds the {@link VirtualDeviceParams} instance.
         *
         * @throws IllegalArgumentException if there's mismatch between policy definition and
         * the passed parameters or if there are sensor configs with the same type and name.
         *
         */
        @NonNull
        public VirtualDeviceParams build() {
            if (!mVirtualSensorConfigs.isEmpty()
                    && (mDevicePolicies.get(POLICY_TYPE_SENSORS, DEVICE_POLICY_DEFAULT)
                            != DEVICE_POLICY_CUSTOM)) {
                throw new IllegalArgumentException(
                        "DEVICE_POLICY_CUSTOM for POLICY_TYPE_SENSORS is required for creating "
                                + "virtual sensors.");
            }
            SparseArray<Set<String>> sensorNameByType = new SparseArray();
            for (int i = 0; i < mVirtualSensorConfigs.size(); ++i) {
                VirtualSensorConfig config = mVirtualSensorConfigs.get(i);
                Set<String> sensorNames = sensorNameByType.get(config.getType(), new ArraySet<>());
                if (!sensorNames.add(config.getName())) {
                    throw new IllegalArgumentException(
                            "Sensor names must be unique for a particular sensor type.");
                }
                sensorNameByType.put(config.getType(), sensorNames);
            }

            return new VirtualDeviceParams(
                    mLockState,
                    mUsersWithMatchingAccounts,
@@ -635,7 +683,8 @@ public final class VirtualDeviceParams implements Parcelable {
                    mBlockedActivities,
                    mDefaultActivityPolicy,
                    mName,
                    mDevicePolicies);
                    mDevicePolicies,
                    mVirtualSensorConfigs);
        }
    }
}
+35 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.companion.virtual.sensor;

/**
 * Interface for notification of listener registration changes for a virtual sensor.
 *
 * @hide
 */
oneway interface IVirtualSensorStateChangeCallback {

    /**
     * Called when the registered listeners to a virtual sensor have changed.
     *
     * @param enabled Whether the sensor is enabled.
     * @param samplingPeriodMicros The requested sensor's sampling period in microseconds.
     * @param batchReportingLatencyMicros The requested maximum time interval in microseconds
     * between the delivery of two batches of sensor events.
     */
    void onStateChanged(boolean enabled, int samplingPeriodMicros, int batchReportLatencyMicros);
}
Loading