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

Commit 000cdd23 authored by Chienyuan Huang's avatar Chienyuan Huang
Browse files

Implement DistanceMeasurementManager in Bluetooth app

Bug: 256055210
Test: atest DistanceMeasurementManagerTest
Test: atest DistanceMeasurementTrackerTest
Tag: #feature
Change-Id: I27f26995e11ed042fdbd35261798b607d212d076
parent 6d2a089f
Loading
Loading
Loading
Loading
+128 −1
Original line number Diff line number Diff line
@@ -190,14 +190,23 @@ static jmethodID method_onSyncLost;
static jmethodID method_onSyncReport;
static jmethodID method_onSyncStarted;
static jmethodID method_onSyncTransferredCallback;

/**
 * Static variables
 * Distance Measurement callback methods
 */
static jmethodID method_onDistanceMeasurementStarted;
static jmethodID method_onDistanceMeasurementStartFail;
static jmethodID method_onDistanceMeasurementStopped;
static jmethodID method_onDistanceMeasurementResult;

/**
 * Static variables
 */
static const btgatt_interface_t* sGattIf = NULL;
static jobject mCallbacksObj = NULL;
static jobject mAdvertiseCallbacksObj = NULL;
static jobject mPeriodicScanCallbacksObj = NULL;
static jobject mDistanceMeasurementCallbacksObj = NULL;
static std::shared_mutex callbacks_mutex;

/**
@@ -1150,6 +1159,63 @@ class JniScanningCallbacks : ScanningCallbacks {
  }
};

class JniDistanceMeasurementCallbacks : DistanceMeasurementCallbacks {
 public:
  static DistanceMeasurementCallbacks* GetInstance() {
    static DistanceMeasurementCallbacks* instance =
        new JniDistanceMeasurementCallbacks();
    return instance;
  }

  void OnDistanceMeasurementStarted(RawAddress address, uint8_t method) {
    std::shared_lock<std::shared_mutex> lock(callbacks_mutex);
    CallbackEnv sCallbackEnv(__func__);
    if (!sCallbackEnv.valid() || !mDistanceMeasurementCallbacksObj) return;
    ScopedLocalRef<jstring> addr(sCallbackEnv.get(),
                                 bdaddr2newjstr(sCallbackEnv.get(), &address));
    sCallbackEnv->CallVoidMethod(mDistanceMeasurementCallbacksObj,
                                 method_onDistanceMeasurementStarted,
                                 addr.get(), method);
  }
  void OnDistanceMeasurementStartFail(RawAddress address, uint8_t reason,
                                      uint8_t method) {
    std::shared_lock<std::shared_mutex> lock(callbacks_mutex);
    CallbackEnv sCallbackEnv(__func__);
    if (!sCallbackEnv.valid() || !mDistanceMeasurementCallbacksObj) return;
    ScopedLocalRef<jstring> addr(sCallbackEnv.get(),
                                 bdaddr2newjstr(sCallbackEnv.get(), &address));
    sCallbackEnv->CallVoidMethod(mDistanceMeasurementCallbacksObj,
                                 method_onDistanceMeasurementStartFail,
                                 addr.get(), reason, method);
  }
  void OnDistanceMeasurementStopped(RawAddress address, uint8_t reason,
                                    uint8_t method) {
    std::shared_lock<std::shared_mutex> lock(callbacks_mutex);
    CallbackEnv sCallbackEnv(__func__);
    if (!sCallbackEnv.valid() || !mDistanceMeasurementCallbacksObj) return;
    ScopedLocalRef<jstring> addr(sCallbackEnv.get(),
                                 bdaddr2newjstr(sCallbackEnv.get(), &address));
    sCallbackEnv->CallVoidMethod(mDistanceMeasurementCallbacksObj,
                                 method_onDistanceMeasurementStopped,
                                 addr.get(), reason, method);
  }

  void OnDistanceMeasurementResult(RawAddress address, uint32_t centimeter,
                                   uint32_t error_centimeter, int azimuth_angle,
                                   int error_azimuth_angle, int altitude_angle,
                                   int error_altitude_angle, uint8_t method) {
    std::shared_lock<std::shared_mutex> lock(callbacks_mutex);
    CallbackEnv sCallbackEnv(__func__);
    if (!sCallbackEnv.valid() || !mDistanceMeasurementCallbacksObj) return;
    ScopedLocalRef<jstring> addr(sCallbackEnv.get(),
                                 bdaddr2newjstr(sCallbackEnv.get(), &address));
    sCallbackEnv->CallVoidMethod(
        mDistanceMeasurementCallbacksObj, method_onDistanceMeasurementResult,
        addr.get(), centimeter, error_centimeter, azimuth_angle,
        error_azimuth_angle, altitude_angle, error_altitude_angle, method);
  }
};

/**
 * Native function definitions
 */
@@ -1309,6 +1375,8 @@ static void initializeNative(JNIEnv* env, jobject object) {
  sGattIf->advertiser->RegisterCallbacks(
      JniAdvertisingCallbacks::GetInstance());
  sGattIf->scanner->RegisterCallbacks(JniScanningCallbacks::GetInstance());
  sGattIf->distance_measurement_manager->RegisterDistanceMeasurementCallbacks(
      JniDistanceMeasurementCallbacks::GetInstance());

  mCallbacksObj = env->NewGlobalRef(object);

@@ -2514,6 +2582,51 @@ static void gattTestNative(JNIEnv* env, jobject object, jint command,
  sGattIf->client->test_command(command, params);
}

static void distanceMeasurementClassInitNative(JNIEnv* env, jclass clazz) {
  method_onDistanceMeasurementStarted = env->GetMethodID(
      clazz, "onDistanceMeasurementStarted", "(Ljava/lang/String;I)V");
  method_onDistanceMeasurementStartFail = env->GetMethodID(
      clazz, "onDistanceMeasurementStartFail", "(Ljava/lang/String;II)V");
  method_onDistanceMeasurementStopped = env->GetMethodID(
      clazz, "onDistanceMeasurementStopped", "(Ljava/lang/String;II)V");
  method_onDistanceMeasurementResult = env->GetMethodID(
      clazz, "onDistanceMeasurementResult", "(Ljava/lang/String;IIIIIII)V");
}

static void distanceMeasurementInitializeNative(JNIEnv* env, jobject object) {
  std::unique_lock<std::shared_mutex> lock(callbacks_mutex);
  if (mDistanceMeasurementCallbacksObj != NULL) {
    ALOGW("Cleaning up Advertise callback object");
    env->DeleteGlobalRef(mDistanceMeasurementCallbacksObj);
    mDistanceMeasurementCallbacksObj = NULL;
  }

  mDistanceMeasurementCallbacksObj = env->NewGlobalRef(object);
}

static void distanceMeasurementCleanupNative(JNIEnv* env, jobject object) {
  std::unique_lock<std::shared_mutex> lock(callbacks_mutex);
  if (mDistanceMeasurementCallbacksObj != NULL) {
    env->DeleteGlobalRef(mDistanceMeasurementCallbacksObj);
    mDistanceMeasurementCallbacksObj = NULL;
  }
}

static void startDistanceMeasurementNative(JNIEnv* env, jobject object,
                                           jstring address, jint frequency,
                                           jint method) {
  if (!sGattIf) return;
  sGattIf->distance_measurement_manager->StartDistanceMeasurement(
      str2addr(env, address), frequency, method);
}

static void stopDistanceMeasurementNative(JNIEnv* env, jobject object,
                                          jstring address, jint method) {
  if (!sGattIf) return;
  sGattIf->distance_measurement_manager->StopDistanceMeasurement(
      str2addr(env, address), method);
}

/**
 * JNI function definitinos
 */
@@ -2592,6 +2705,17 @@ static JNINativeMethod sScanMethods[] = {
     (void*)gattSetScanParametersNative},
};

// JNI functions defined in DistanceMeasurementManager class.
static JNINativeMethod sDistanceMeasurementMethods[] = {
    {"classInitNative", "()V", (void*)distanceMeasurementClassInitNative},
    {"initializeNative", "()V", (void*)distanceMeasurementInitializeNative},
    {"cleanupNative", "()V", (void*)distanceMeasurementCleanupNative},
    {"startDistanceMeasurementNative", "(Ljava/lang/String;II)V",
     (void*)startDistanceMeasurementNative},
    {"stopDistanceMeasurementNative", "(Ljava/lang/String;I)V",
     (void*)stopDistanceMeasurementNative},
};

// JNI functions defined in GattNativeInterface class.
static JNINativeMethod sMethods[] = {
    {"classInitNative", "()V", (void*)classInitNative},
@@ -2678,6 +2802,9 @@ int register_com_android_bluetooth_gatt(JNIEnv* env) {
  register_success &= jniRegisterNativeMethods(
      env, "com/android/bluetooth/gatt/PeriodicScanManager",
      sPeriodicScanMethods, NELEM(sPeriodicScanMethods));
  register_success &= jniRegisterNativeMethods(
      env, "com/android/bluetooth/gatt/DistanceMeasurementNativeInterface",
      sDistanceMeasurementMethods, NELEM(sDistanceMeasurementMethods));
  return register_success &
         jniRegisterNativeMethods(
             env, "com/android/bluetooth/gatt/GattNativeInterface", sMethods,
+344 −0
Original line number Diff line number Diff line
/*
 * Copyright 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 com.android.bluetooth.gatt;

import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothStatusCodes;
import android.bluetooth.BluetoothUtils;
import android.bluetooth.le.DistanceMeasurementMethod;
import android.bluetooth.le.DistanceMeasurementParams;
import android.bluetooth.le.DistanceMeasurementResult;
import android.bluetooth.le.IDistanceMeasurementCallback;
import android.os.HandlerThread;
import android.os.RemoteException;
import android.util.Log;

import com.android.bluetooth.btservice.AdapterService;
import com.android.internal.annotations.VisibleForTesting;

import java.util.HashSet;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Manages distnace measurement operations and interacts with Gabeldorsche stack.
 *
 * @hide
 */
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public class DistanceMeasurementManager {
    private static final boolean DBG = GattServiceConfig.DBG;
    private static final String TAG = "DistanceMeasurementManager";

    private static final int RSSI_FREQUENCY_LOW = 3000;
    private static final int RSSI_FREQUENCY_MEDIUM = 1000;
    private static final int RSSI_FREQUENCY_HIGH = 500;

    private final AdapterService mAdapterService;
    private HandlerThread mHandlerThread;
    DistanceMeasurementNativeInterface mDistanceMeasurementNativeInterface;
    private ConcurrentHashMap<String, HashSet<DistanceMeasurementTracker>> mRssiTrackers =
            new ConcurrentHashMap<>();

    /**
     * Constructor of {@link DistanceMeasurementManager}.
     */
    DistanceMeasurementManager(AdapterService adapterService) {
        mAdapterService = adapterService;
    }

    /**
     * Start a {@link HandlerThread} that handles distnace measurement operations.
     */
    void start() {
        mHandlerThread = new HandlerThread("DistanceMeasurementManager");
        mHandlerThread.start();
        mDistanceMeasurementNativeInterface = DistanceMeasurementNativeInterface.getInstance();
        mDistanceMeasurementNativeInterface.init(this);
    }

    void cleanup() {
        mDistanceMeasurementNativeInterface.cleanup();
    }

    DistanceMeasurementMethod[] getSupportedDistanceMeasurementMethods() {
        DistanceMeasurementMethod rssi = new DistanceMeasurementMethod.Builder(
                DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_RSSI).build();
        DistanceMeasurementMethod[] methods = {rssi};
        return methods;
    }


    void startDistanceMeasurement(UUID uuid, DistanceMeasurementParams params,
            IDistanceMeasurementCallback callback) {
        Log.i(TAG, "startDistanceMeasurement device:" + params.getDevice().getAnonymizedAddress()
                + ", method: " + params.getMethod());
        String identityAddress = mAdapterService.getIdentityAddress(
                params.getDevice().getAddress());
        logd("Get identityAddress: " + params.getDevice().getAnonymizedAddress() + " => "
                + BluetoothUtils.toAnonymizedAddress(identityAddress));

        int frequencyValue = getFrequencyValue(params.getFrequency(), params.getMethod());
        if (frequencyValue == -1) {
            invokeStartFail(callback, params.getDevice(),
                    BluetoothStatusCodes.ERROR_BAD_PARAMETERS);
            return;
        }

        DistanceMeasurementTracker tracker = new DistanceMeasurementTracker(
                this, params, identityAddress, uuid, frequencyValue, callback);

        switch (params.getMethod()) {
            case DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_AUTO:
            case DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_RSSI:
                startRssiTracker(tracker);
                break;
            default:
                invokeStartFail(callback, params.getDevice(),
                        BluetoothStatusCodes.ERROR_BAD_PARAMETERS);
        }
    }

    private synchronized void startRssiTracker(DistanceMeasurementTracker tracker) {
        mRssiTrackers.putIfAbsent(tracker.mIdentityAddress,
                new HashSet<DistanceMeasurementTracker>());
        HashSet<DistanceMeasurementTracker> set = mRssiTrackers.get(tracker.mIdentityAddress);
        if (set.contains(tracker)) {
            Log.w(TAG, "Already registered");
            return;
        }
        set.add(tracker);
        mDistanceMeasurementNativeInterface.startDistanceMeasurement(tracker.mIdentityAddress,
                tracker.mFrequency, DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_RSSI);
    }

    int stopDistanceMeasurement(UUID uuid, BluetoothDevice device, int method,
            boolean timeout) {
        Log.i(TAG, "stopDistanceMeasurement device:" + device.getAnonymizedAddress()
                + ", method: " + method + " timeout " + timeout);
        String identityAddress = mAdapterService.getIdentityAddress(device.getAddress());
        logd("Get identityAddress: " + device.getAnonymizedAddress() + " => "
                + BluetoothUtils.toAnonymizedAddress(identityAddress));

        switch (method) {
            case DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_AUTO:
            case DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_RSSI:
                return stopRssiTracker(uuid, identityAddress, timeout);
            default:
                Log.w(TAG, "stopDistanceMeasurement with invalid method:" + method);
                return BluetoothStatusCodes.DISTANCE_MEASUREMENT_ERROR_INTERNAL;
        }
    }

    private synchronized int stopRssiTracker(UUID uuid, String identityAddress,
            boolean timeout) {
        HashSet<DistanceMeasurementTracker> set = mRssiTrackers.get(identityAddress);
        if (set == null) {
            Log.w(TAG, "Can't find rssi tracker");
            return BluetoothStatusCodes.DISTANCE_MEASUREMENT_ERROR_INTERNAL;
        }

        for (DistanceMeasurementTracker tracker : set) {
            if (tracker.equals(uuid, identityAddress)) {
                int reason = timeout ? BluetoothStatusCodes.ERROR_TIMEOUT :
                        BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST;
                invokeOnStopped(tracker.mCallback, tracker.mDevice, reason);
                tracker.cancelTimer();
                set.remove(tracker);
                break;
            }
        }

        if (set.isEmpty()) {
            logd("no rssi tracker");
            mRssiTrackers.remove(identityAddress);
            mDistanceMeasurementNativeInterface.stopDistanceMeasurement(identityAddress,
                    DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_RSSI);
        }
        return BluetoothStatusCodes.SUCCESS;
    }

    private void invokeStartFail(IDistanceMeasurementCallback callback, BluetoothDevice device,
            int reason) {
        try {
            callback.onStartFail(device, reason);
        } catch (RemoteException e) {
            Log.e(TAG, "Exception: " + e);
        }
    }

    private void invokeOnStopped(IDistanceMeasurementCallback callback, BluetoothDevice device,
            int reason) {
        try {
            callback.onStopped(device, reason);
        } catch (RemoteException e) {
            Log.e(TAG, "Exception: " + e);
        }
    }

    /**
     * Convert frequency into value in ms
     */
    private int getFrequencyValue(int frequency, int method) {
        switch (method) {
            case DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_AUTO:
            case DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_RSSI:
                switch (frequency) {
                    case DistanceMeasurementParams.REPORT_FREQUENCY_LOW:
                        return RSSI_FREQUENCY_LOW;
                    case DistanceMeasurementParams.REPORT_FREQUENCY_MEDIUM:
                        return RSSI_FREQUENCY_MEDIUM;
                    case DistanceMeasurementParams.REPORT_FREQUENCY_HIGH:
                        return RSSI_FREQUENCY_HIGH;
                }
                break;
            default:

        }
        Log.w(TAG, "getFrequencyValue fail frequency:" + frequency + ", method:" + method);
        return -1;
    }

    void onDistanceMeasurementStarted(String address, int method) {
        logd("onDistanceMeasurementStarted address:" + BluetoothUtils.toAnonymizedAddress(address)
                + ", method:" + method);
        switch (method) {
            case DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_RSSI:
                handleRssiStarted(address);
                break;
            default:
                Log.w(TAG, "onDistanceMeasurementResult: invalid method " + method);
        }
    }

    void handleRssiStarted(String address) {
        HashSet<DistanceMeasurementTracker> set = mRssiTrackers.get(address);
        if (set == null) {
            Log.w(TAG, "Can't find rssi tracker");
            return;
        }
        for (DistanceMeasurementTracker tracker : set) {
            try {
                if (!tracker.mStarted) {
                    tracker.mStarted = true;
                    tracker.mCallback.onStarted(tracker.mDevice);
                    tracker.startTimer(mHandlerThread.getLooper());
                }
            } catch (RemoteException e) {
                Log.e(TAG, "Exception: " + e);
            }
        }
    }

    void onDistanceMeasurementStartFail(String address, int reason, int method) {
        logd("onDistanceMeasurementStartFail address:" + BluetoothUtils.toAnonymizedAddress(address)
                + ", method:" + method);
        switch (method) {
            case DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_RSSI:
                handleRssiStartFail(address, reason);
                break;
            default:
                Log.w(TAG, "onDistanceMeasurementStartFail: invalid method " + method);
        }
    }

    void handleRssiStartFail(String address, int reason) {
        HashSet<DistanceMeasurementTracker> set = mRssiTrackers.get(address);
        if (set == null) {
            Log.w(TAG, "Can't find rssi tracker");
            return;
        }
        for (DistanceMeasurementTracker tracker : set) {
            if (!tracker.mStarted) {
                invokeStartFail(tracker.mCallback, tracker.mDevice, reason);
            }
        }
        synchronized (set) {
            set.removeIf(tracker -> !tracker.mStarted);
        }
    }

    void onDistanceMeasurementStopped(String address, int reason, int method) {
        logd("onDistanceMeasurementStopped address:" + BluetoothUtils.toAnonymizedAddress(address)
                + ", reason:" + reason + ", method:" + method);
        switch (method) {
            case DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_RSSI:
                handleRssiStopped(address, reason);
                break;
            default:
                Log.w(TAG, "onDistanceMeasurementStopped: invalid method " + method);
        }
    }

    void handleRssiStopped(String address, int reason) {
        HashSet<DistanceMeasurementTracker> set = mRssiTrackers.get(address);
        if (set == null) {
            Log.w(TAG, "Can't find rssi tracker");
            return;
        }
        for (DistanceMeasurementTracker tracker : set) {
            if (tracker.mStarted) {
                tracker.cancelTimer();
                invokeOnStopped(tracker.mCallback, tracker.mDevice, reason);
            }
        }
        synchronized (set) {
            set.removeIf(tracker -> tracker.mStarted);
        }
    }

    void onDistanceMeasurementResult(String address, int centimeter, int errorCentimeter,
            int azimuthAngle, int errorAzimuthAngle, int altitudeAngle, int errorAltitudeAngle,
            int method) {
        logd("onDistanceMeasurementResult " + BluetoothUtils.toAnonymizedAddress(address)
                + ", centimeter " + centimeter);
        switch (method) {
            case DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_RSSI:
                DistanceMeasurementResult result = new DistanceMeasurementResult.Builder(
                        centimeter / 100.0, errorCentimeter / 100.0).build();
                handleRssiResult(address, result);
                break;
            default:
                Log.w(TAG, "onDistanceMeasurementResult: invalid method " + method);
        }
    }

    void handleRssiResult(String address, DistanceMeasurementResult result) {
        HashSet<DistanceMeasurementTracker> set = mRssiTrackers.get(address);
        if (set == null) {
            Log.w(TAG, "Can't find rssi tracker");
            return;
        }
        for (DistanceMeasurementTracker tracker : set) {
            try {
                if (!tracker.mStarted) {
                    continue;
                }
                tracker.mCallback.onResult(tracker.mDevice, result);
            } catch (RemoteException e) {
                Log.e(TAG, "Exception: " + e);
            }
        }
    }

    /** Logs the message in debug ROM. */
    private static void logd(String msg) {
        if (DBG) {
            Log.d(TAG, msg);
        }
    }
}
+98 −0
Original line number Diff line number Diff line
/*
 * Copyright 2023 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 com.android.bluetooth.gatt;

import com.android.internal.annotations.VisibleForTesting;

/**
 * Distance Measurement Native Interface to/from JNI.
 *
 * @hide
 */
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public class DistanceMeasurementNativeInterface {
    private static DistanceMeasurementNativeInterface sInterface;
    private static final Object INSTANCE_LOCK = new Object();
    private DistanceMeasurementManager mDistanceMeasurementManager;

    private DistanceMeasurementNativeInterface() {}

    /**
     * This class is a singleton because native library should only be loaded once
     *
     * @return default instance
     */
    public static DistanceMeasurementNativeInterface getInstance() {
        synchronized (INSTANCE_LOCK) {
            if (sInterface == null) {
                sInterface = new DistanceMeasurementNativeInterface();
            }
        }
        return sInterface;
    }

    void init(DistanceMeasurementManager manager) {
        mDistanceMeasurementManager = manager;
        initializeNative();
    }

    void cleanup() {
        cleanupNative();
    }

    void startDistanceMeasurement(String address, int frequency, int method) {
        startDistanceMeasurementNative(address, frequency, method);
    }

    void stopDistanceMeasurement(String address, int method) {
        stopDistanceMeasurementNative(address, method);
    }

    void onDistanceMeasurementStarted(String address, int method) {
        mDistanceMeasurementManager.onDistanceMeasurementStarted(address, method);
    }

    void onDistanceMeasurementStartFail(String address, int reason, int method) {
        mDistanceMeasurementManager.onDistanceMeasurementStartFail(address, reason, method);
    }

    void onDistanceMeasurementStopped(String address, int reason, int method) {
        mDistanceMeasurementManager.onDistanceMeasurementStopped(address, reason, method);
    }

    void onDistanceMeasurementResult(String address, int centimeter, int errorCentimeter,
            int azimuthAngle, int errorAzimuthAngle, int altitudeAngle, int errorAltitudeAngle,
            int method) {
        mDistanceMeasurementManager.onDistanceMeasurementResult(address, centimeter,
                errorCentimeter, azimuthAngle, errorAzimuthAngle, altitudeAngle, errorAltitudeAngle,
                method);
    }

    static {
        classInitNative();
    }

    private static native void classInitNative();

    private native void initializeNative();

    private native void cleanupNative();

    private native void startDistanceMeasurementNative(String address, int frequency, int method);

    private native void stopDistanceMeasurementNative(String address, int method);
}
+108 −0

File added.

Preview size limit exceeded, changes collapsed.

+18 −14

File changed.

Preview size limit exceeded, changes collapsed.

Loading