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

Commit 9517abfe authored by Chienyuan Huang's avatar Chienyuan Huang Committed by Gerrit Code Review
Browse files

Merge "Implement DistanceMeasurementManager in Bluetooth app"

parents 7a5ece09 000cdd23
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