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

Commit 8a6a4a45 authored by William Escande's avatar William Escande Committed by Gerrit Code Review
Browse files

Merge changes I9881edb1,Ic1741d77 into main

* changes:
  Always initialize the AirplaneModeListener
  Implement SatelliteModeListener in kt + FLAG
parents 0075041e e67af197
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ filegroup {
        "src/AdapterState.kt",
        "src/RadioModeListener.kt",
        "src/com/**/*.kt",
        "src/satellite/ModeListener.kt",
    ],
    visibility: [":__subpackages__"],
}
@@ -164,6 +165,8 @@ android_robolectric_test {
        "src/AdapterStateTest.kt",
        "src/RadioModeListener.kt",
        "src/RadioModeListenerTest.kt",
        "src/satellite/ModeListener.kt",
        "src/satellite/ModeListenerTest.kt",
    ],

    static_libs: [
+11 −1
Original line number Diff line number Diff line
@@ -103,9 +103,19 @@ class BluetoothAirplaneModeListener extends Handler {
        mNotificationManager = notificationManager;
        mContext = context;

        String airplaneModeRadios =
                Settings.Global.getString(
                        mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_RADIOS);
        if (airplaneModeRadios != null
                && !airplaneModeRadios.contains(Settings.Global.RADIO_BLUETOOTH)) {
            Log.w(TAG, "BluetoothAirplaneModeListener: blocked by AIRPLANE_MODE_RADIOS");
            mIsAirplaneModeOn = false;
            return;
        }

        mIsAirplaneModeOn = isGlobalAirplaneModeOn(mContext);

        context.getContentResolver()
        mContext.getContentResolver()
                .registerContentObserver(
                        Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON),
                        true,
+36 −16
Original line number Diff line number Diff line
@@ -74,6 +74,7 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.sysprop.BluetoothProperties;
@@ -84,6 +85,9 @@ import com.android.bluetooth.BluetoothStatsLog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.BluetoothManagerServiceDumpProto;
import com.android.server.bluetooth.satellite.SatelliteModeListener;

import kotlin.Unit;

import java.io.FileDescriptor;
import java.io.FileOutputStream;
@@ -183,6 +187,7 @@ class BluetoothManagerService {
    private static final int DEFAULT_APM_ENHANCEMENT_STATE = 1;

    private final Context mContext;
    private final Looper mLooper;

    private final UserManager mUserManager;

@@ -211,12 +216,20 @@ class BluetoothManagerService {

    private BluetoothModeChangeHelper mBluetoothModeChangeHelper;

    private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener;
    private final BluetoothAirplaneModeListener mBluetoothAirplaneModeListener;

    private BluetoothNotificationManager mBluetoothNotificationManager;

    // TODO(b/289584302): remove BluetoothSatelliteModeListener once use_new_satellite_mode ship
    private BluetoothSatelliteModeListener mBluetoothSatelliteModeListener;

    // TODO(b/289584302): Use aconfig flag when available on AOSP
    private static final boolean USE_NEW_SATELLITE_MODE =
            DeviceConfig.getBoolean(
                    DeviceConfig.NAMESPACE_BLUETOOTH,
                    "com.android.bluetooth.use_new_satellite_mode",
                    false);

    // used inside handler thread
    private boolean mQuietEnable = false;
    private boolean mEnable = false;
@@ -443,8 +456,9 @@ class BluetoothManagerService {
                0);
    }

    // TODO(b/289584302): Update to private once use_new_satellite_mode is enabled
    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
    void onSatelliteModeChanged(boolean isSatelliteModeOn) {
    Unit onSatelliteModeChanged(boolean isSatelliteModeOn) {
        mHandler.postDelayed(
                () ->
                        delayModeChangedIfNeeded(
@@ -453,6 +467,7 @@ class BluetoothManagerService {
                                "onSatelliteModeChanged"),
                ON_SATELLITE_MODE_CHANGED_TOKEN,
                0);
        return Unit.INSTANCE;
    }

    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
@@ -640,7 +655,7 @@ class BluetoothManagerService {

    BluetoothManagerService(@NonNull Context context, @NonNull Looper looper) {
        mContext = requireNonNull(context, "Context cannot be null");
        requireNonNull(looper, "Looper cannot be null");
        mLooper = requireNonNull(looper, "Looper cannot be null");

        mUserManager =
                requireNonNull(
@@ -648,7 +663,7 @@ class BluetoothManagerService {
                        "UserManager system service cannot be null");

        mBinder = new BluetoothServiceBinder(this, mContext, mUserManager);
        mHandler = new BluetoothHandler(looper);
        mHandler = new BluetoothHandler(mLooper);

        mContentResolver = mContext.getContentResolver();

@@ -717,17 +732,14 @@ class BluetoothManagerService {
            mEnableExternal = true;
        }

        String airplaneModeRadios =
                Settings.Global.getString(mContentResolver, Settings.Global.AIRPLANE_MODE_RADIOS);
        if (airplaneModeRadios == null
                || airplaneModeRadios.contains(Settings.Global.RADIO_BLUETOOTH)) {
        mBluetoothAirplaneModeListener =
                new BluetoothAirplaneModeListener(
                            this, looper, mContext, mBluetoothNotificationManager);
        }
                        this, mLooper, mContext, mBluetoothNotificationManager);

        if (!USE_NEW_SATELLITE_MODE) {
            mBluetoothSatelliteModeListener =
                new BluetoothSatelliteModeListener(this, looper, mContext);
                    new BluetoothSatelliteModeListener(this, mLooper, mContext);
        }
    }

    IBluetoothManager.Stub getBinder() {
@@ -736,12 +748,14 @@ class BluetoothManagerService {

    /** Returns true if airplane mode is currently on */
    private boolean isAirplaneModeOn() {
        return mBluetoothAirplaneModeListener != null
            && mBluetoothAirplaneModeListener.isAirplaneModeOn();
        return mBluetoothAirplaneModeListener.isAirplaneModeOn();
    }

    /** Returns true if satellite mode is turned on. */
    private boolean isSatelliteModeOn() {
        if (USE_NEW_SATELLITE_MODE) {
            return SatelliteModeListener.isOn();
        }
        return mBluetoothSatelliteModeListener.isSatelliteModeOn();
    }

@@ -1486,6 +1500,12 @@ class BluetoothManagerService {
        if (DBG) {
            Log.d(TAG, "Bluetooth boot completed");
        }

        if (USE_NEW_SATELLITE_MODE) {
            SatelliteModeListener.initialize(
                    mLooper, mContentResolver, this::onSatelliteModeChanged);
        }

        final boolean isBluetoothDisallowed = isBluetoothDisallowed();
        if (isBluetoothDisallowed) {
            return;
+64 −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.
 */
@file:JvmName("SatelliteModeListener")

package com.android.server.bluetooth.satellite

import android.content.ContentResolver
import android.os.Looper
import android.util.Log
import com.android.server.bluetooth.initializeRadioModeListener

/**
 * constant copied from {@link Settings.Global}
 *
 * TODO(b/274636414): Migrate to official API in Android V.
 */
internal const val SETTINGS_SATELLITE_MODE_RADIOS = "satellite_mode_radios"

/**
 * constant copied from {@link Settings.Global}
 *
 * TODO(b/274636414): Migrate to official API in Android V.
 */
internal const val SETTINGS_SATELLITE_MODE_ENABLED = "satellite_mode_enabled"

private const val TAG = "BluetoothSatelliteModeListener"

public var isOn = false
    private set

/** Listen on satellite mode and trigger the callback if it has changed */
public fun initialize(looper: Looper, resolver: ContentResolver, callback: (m: Boolean) -> Unit) {
    val satellite_callback =
        fun(newMode: Boolean) {
            val previousMode = isOn
            isOn = newMode
            if (previousMode == isOn) {
                Log.d(TAG, "Ignore satellite mode change because is already: " + isOn)
                return
            }
            callback(isOn)
        }
    isOn =
        initializeRadioModeListener(
            looper,
            resolver,
            SETTINGS_SATELLITE_MODE_RADIOS,
            SETTINGS_SATELLITE_MODE_ENABLED,
            satellite_callback
        )
}
+195 −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.server.bluetooth.satellite.test

import android.content.ContentResolver
import android.content.Context
import android.os.Looper
import android.provider.Settings
import androidx.test.core.app.ApplicationProvider
import com.android.server.bluetooth.satellite.SETTINGS_SATELLITE_MODE_ENABLED
import com.android.server.bluetooth.satellite.SETTINGS_SATELLITE_MODE_RADIOS
import com.android.server.bluetooth.satellite.initialize
import com.android.server.bluetooth.satellite.isOn
import com.android.server.bluetooth.test.disableMode
import com.android.server.bluetooth.test.disableSensitive
import com.android.server.bluetooth.test.enableMode
import com.android.server.bluetooth.test.enableSensitive
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.times
import org.robolectric.RobolectricTestRunner

@RunWith(RobolectricTestRunner::class)
class ModeListenerTest {
    private val resolver: ContentResolver =
        ApplicationProvider.getApplicationContext<Context>().getContentResolver()

    private val looper: Looper = Looper.getMainLooper()

    private lateinit var mode: ArrayList<Boolean>

    @Before
    public fun setup() {
        mode = ArrayList()
    }

    private fun enableSensitive() {
        enableSensitive(resolver, looper, SETTINGS_SATELLITE_MODE_RADIOS)
    }

    private fun disableSensitive() {
        disableSensitive(resolver, looper, SETTINGS_SATELLITE_MODE_RADIOS)
    }

    private fun disableMode() {
        disableMode(resolver, looper, SETTINGS_SATELLITE_MODE_ENABLED)
    }

    private fun enableMode() {
        enableMode(resolver, looper, SETTINGS_SATELLITE_MODE_ENABLED)
    }

    private fun callback(newMode: Boolean) = mode.add(newMode)

    @Test
    fun initialize_whenNullSensitive_isOff() {
        Settings.Global.putString(resolver, SETTINGS_SATELLITE_MODE_RADIOS, null)
        enableMode()

        initialize(looper, resolver, this::callback)

        assertThat(isOn).isFalse()
        assertThat(mode).isEmpty()
    }

    @Test
    fun initialize_whenNotSensitive_isOff() {
        disableSensitive()
        enableMode()

        initialize(looper, resolver, this::callback)

        assertThat(isOn).isFalse()
        assertThat(mode).isEmpty()
    }

    @Test
    fun enable_whenNotSensitive_isOff() {
        disableSensitive()
        disableMode()

        initialize(looper, resolver, this::callback)

        enableMode()

        assertThat(isOn).isFalse()
        assertThat(mode).isEmpty()
    }

    @Test
    fun initialize_whenSensitive_isOff() {
        enableSensitive()
        disableMode()

        initialize(looper, resolver, this::callback)

        assertThat(isOn).isFalse()
        assertThat(mode).isEmpty()
    }

    @Test
    fun initialize_whenSensitive_isOn() {
        enableSensitive()
        enableMode()

        initialize(looper, resolver, this::callback)

        assertThat(isOn).isTrue()
        assertThat(mode).isEmpty()
    }

    @Test
    fun toggleSensitive_whenEnabled_isOnOffOn() {
        enableSensitive()
        enableMode()

        initialize(looper, resolver, this::callback)

        disableSensitive()
        enableSensitive()

        assertThat(isOn).isTrue()
        assertThat(mode).containsExactly(false, true)
    }

    @Test
    fun toggleEnable_whenSensitive_isOffOnOff() {
        enableSensitive()
        disableMode()

        initialize(looper, resolver, this::callback)

        enableMode()
        disableMode()

        assertThat(isOn).isFalse()
        assertThat(mode).containsExactly(true, false)
    }

    @Test
    fun disable_whenDisabled_discardUpdate() {
        enableSensitive()
        disableMode()

        initialize(looper, resolver, this::callback)

        disableMode()

        assertThat(isOn).isFalse()
        assertThat(mode).isEmpty()
    }

    @Test
    fun enabled_whenEnabled_discardOnChange() {
        enableSensitive()
        enableMode()

        initialize(looper, resolver, this::callback)

        enableMode()

        assertThat(isOn).isTrue()
        assertThat(mode).isEmpty()
    }

    @Test
    fun changeContent_whenDisabled_discard() {
        enableSensitive()
        disableMode()

        initialize(looper, resolver, this::callback)

        disableSensitive()
        enableMode()

        assertThat(isOn).isFalse()
        // As opposed to the bare RadioModeListener, similar consecutive event are discarded
        assertThat(mode).isEmpty()
    }
}
Loading