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

Commit 4604c411 authored by Mehdi Alizadeh's avatar Mehdi Alizadeh Committed by Android (Google) Code Review
Browse files

Merge changes from topic "surface_num_clients_soft_ap"

* changes:
  Adds unregisterSoftApCallback API
  Unit tests for registerSoftApCallback API
  Adds API to register callback for soft AP event
parents 265fc005 55f3aae0
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -548,6 +548,7 @@ java_library {
        "telephony/java/com/android/internal/telephony/euicc/ISetDefaultSmdpAddressCallback.aidl",
        "telephony/java/com/android/internal/telephony/euicc/ISetNicknameCallback.aidl",
        "telephony/java/com/android/internal/telephony/euicc/ISwitchToProfileCallback.aidl",
        "wifi/java/android/net/wifi/ISoftApCallback.aidl",
        "wifi/java/android/net/wifi/IWifiManager.aidl",
        "wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl",
        "wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl",
+44 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.net.wifi;

/**
 * Interface for Soft AP callback.
 *
 * @hide
 */
oneway interface ISoftApCallback
{
    /**
     * Service to manager callback providing current soft AP state. The possible
     * parameter values listed are defined in WifiManager.java
     *
     * @param state new AP state. One of WIFI_AP_STATE_DISABLED,
     *        WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED,
     *        WIFI_AP_STATE_ENABLING, WIFI_AP_STATE_FAILED
     * @param failureReason reason when in failed state. One of
     *        SAP_START_FAILURE_GENERAL, SAP_START_FAILURE_NO_CHANNEL
     */
    void onStateChanged(int state, int failureReason);

    /**
     * Service to manager callback providing number of connected clients.
     *
     * @param numClients number of connected clients
     */
    void onNumClientsChanged(int numClients);
}
+11 −7
Original line number Diff line number Diff line
@@ -23,15 +23,15 @@ import android.net.wifi.hotspot2.OsuProvider;
import android.net.wifi.hotspot2.PasspointConfiguration;
import android.net.wifi.hotspot2.IProvisioningCallback;

import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.ScanSettings;
import android.net.wifi.ScanResult;
import android.net.DhcpInfo;
import android.net.Network;
import android.net.wifi.ISoftApCallback;
import android.net.wifi.PasspointManagementObjectDefinition;
import android.net.wifi.ScanResult;
import android.net.wifi.ScanSettings;
import android.net.wifi.WifiActivityEnergyInfo;
import android.net.Network;

import android.net.DhcpInfo;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;

import android.os.Messenger;
import android.os.ResultReceiver;
@@ -178,5 +178,9 @@ interface IWifiManager
    void restoreSupplicantBackupData(in byte[] supplicantData, in byte[] ipConfigData);

    void startSubscriptionProvisioning(in OsuProvider provider, in IProvisioningCallback callback);

    void registerSoftApCallback(in IBinder binder, in ISoftApCallback callback, int callbackIdentifier);

    void unregisterSoftApCallback(int callbackIdentifier);
}
+135 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.net.wifi;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -55,6 +56,8 @@ import com.android.server.net.NetworkPinner;

import dalvik.system.CloseGuard;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.net.InetAddress;
import java.util.Collections;
@@ -432,6 +435,17 @@ public class WifiManager {
     */
    public static final String EXTRA_WIFI_AP_MODE = "wifi_ap_mode";

    /** @hide */
    @IntDef(flag = false, prefix = { "WIFI_AP_STATE_" }, value = {
        WIFI_AP_STATE_DISABLING,
        WIFI_AP_STATE_DISABLED,
        WIFI_AP_STATE_ENABLING,
        WIFI_AP_STATE_ENABLED,
        WIFI_AP_STATE_FAILED,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface WifiApState {}

    /**
     * Wi-Fi AP is currently being disabled. The state will change to
     * {@link #WIFI_AP_STATE_DISABLED} if it finishes successfully.
@@ -486,6 +500,14 @@ public class WifiManager {
    @SystemApi
    public static final int WIFI_AP_STATE_FAILED = 14;

    /** @hide */
    @IntDef(flag = false, prefix = { "SAP_START_FAILURE_" }, value = {
        SAP_START_FAILURE_GENERAL,
        SAP_START_FAILURE_NO_CHANNEL,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface SapStartFailure {}

    /**
     *  If WIFI AP start failed, this reason code means there is no legal channel exists on
     *  user selected band by regulatory
@@ -2323,6 +2345,119 @@ public class WifiManager {
        public void onFailure(int reason);
    }

    /**
     * Base class for soft AP callback. Should be extended by applications and set when calling
     * {@link WifiManager#registerSoftApCallback(SoftApCallback, Handler)}.
     *
     * @hide
     */
    public interface SoftApCallback {
        /**
         * Called when soft AP state changes.
         *
         * @param state new new AP state. One of {@link #WIFI_AP_STATE_DISABLED},
         *        {@link #WIFI_AP_STATE_DISABLING}, {@link #WIFI_AP_STATE_ENABLED},
         *        {@link #WIFI_AP_STATE_ENABLING}, {@link #WIFI_AP_STATE_FAILED}
         * @param failureReason reason when in failed state. One of
         *        {@link #SAP_START_FAILURE_GENERAL}, {@link #SAP_START_FAILURE_NO_CHANNEL}
         */
        public abstract void onStateChanged(@WifiApState int state,
                @SapStartFailure int failureReason);

        /**
         * Called when number of connected clients to soft AP changes.
         *
         * @param numClients number of connected clients
         */
        public abstract void onNumClientsChanged(int numClients);
    }

    /**
     * Callback proxy for SoftApCallback objects.
     *
     * @hide
     */
    private static class SoftApCallbackProxy extends ISoftApCallback.Stub {
        private final Handler mHandler;
        private final SoftApCallback mCallback;

        SoftApCallbackProxy(Looper looper, SoftApCallback callback) {
            mHandler = new Handler(looper);
            mCallback = callback;
        }

        @Override
        public void onStateChanged(int state, int failureReason) throws RemoteException {
            Log.v(TAG, "SoftApCallbackProxy: onStateChanged: state=" + state + ", failureReason=" +
                    failureReason);
            mHandler.post(() -> {
                mCallback.onStateChanged(state, failureReason);
            });
        }

        @Override
        public void onNumClientsChanged(int numClients) throws RemoteException {
            Log.v(TAG, "SoftApCallbackProxy: onNumClientsChanged: numClients=" + numClients);
            mHandler.post(() -> {
                mCallback.onNumClientsChanged(numClients);
            });
        }
    }

    /**
     * Registers a callback for Soft AP. See {@link SoftApCallback}. Caller will receive the current
     * soft AP state and number of connected devices immediately after a successful call to this API
     * via callback. Note that receiving an immediate WIFI_AP_STATE_FAILED value for soft AP state
     * indicates that the latest attempt to start soft AP has failed. Caller can unregister a
     * previously registered callback using {@link unregisterSoftApCallback}
     * <p>
     * Applications should have the
     * {@link android.Manifest.permission#NETWORK_SETTINGS NETWORK_SETTINGS} permission. Callers
     * without the permission will trigger a {@link java.lang.SecurityException}.
     * <p>
     *
     * @param callback Callback for soft AP events
     * @param handler  The Handler on whose thread to execute the callbacks of the {@code callback}
     *                 object. If null, then the application's main thread will be used.
     *
     * @hide
     */
    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
    public void registerSoftApCallback(@NonNull SoftApCallback callback,
                                       @Nullable Handler handler) {
        if (callback == null) throw new IllegalArgumentException("callback cannot be null");
        Log.v(TAG, "registerSoftApCallback: callback=" + callback + ", handler=" + handler);

        Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper();
        Binder binder = new Binder();
        try {
            mService.registerSoftApCallback(binder, new SoftApCallbackProxy(looper, callback),
                    callback.hashCode());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Allow callers to unregister a previously registered callback. After calling this method,
     * applications will no longer receive soft AP events.
     *
     * @param callback Callback to unregister for soft AP events
     *
     * @hide
     */
    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
    public void unregisterSoftApCallback(@NonNull SoftApCallback callback) {
        if (callback == null) throw new IllegalArgumentException("callback cannot be null");
        Log.v(TAG, "unregisterSoftApCallback: callback=" + callback);

        try {
            mService.unregisterSoftApCallback(callback.hashCode());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * LocalOnlyHotspotReservation that contains the {@link WifiConfiguration} for the active
     * LocalOnlyHotspot request.
+153 −0
Original line number Diff line number Diff line
@@ -24,11 +24,19 @@ import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_INCOMP
import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_NO_CHANNEL;
import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_TETHERING_DISALLOWED;
import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.REQUEST_REGISTERED;
import static android.net.wifi.WifiManager.SAP_START_FAILURE_GENERAL;
import static android.net.wifi.WifiManager.SAP_START_FAILURE_NO_CHANNEL;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.*;

import android.content.Context;
@@ -37,6 +45,7 @@ import android.net.wifi.WifiManager.LocalOnlyHotspotCallback;
import android.net.wifi.WifiManager.LocalOnlyHotspotObserver;
import android.net.wifi.WifiManager.LocalOnlyHotspotReservation;
import android.net.wifi.WifiManager.LocalOnlyHotspotSubscription;
import android.net.wifi.WifiManager.SoftApCallback;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -66,6 +75,7 @@ public class WifiManagerTest {
    @Mock ApplicationInfo mApplicationInfo;
    @Mock WifiConfiguration mApConfig;
    @Mock IBinder mAppBinder;
    @Mock SoftApCallback mSoftApCallback;

    private Handler mHandler;
    private TestLooper mLooper;
@@ -631,6 +641,149 @@ public class WifiManagerTest {
        mWifiManager.watchLocalOnlyHotspot(observer, mHandler);
    }

    /**
     * Verify an IllegalArgumentException is thrown if callback is not provided.
     */
    @Test
    public void registerSoftApCallbackThrowsIllegalArgumentExceptionOnNullArgumentForCallback() {
        try {
            mWifiManager.registerSoftApCallback(null, mHandler);
            fail("expected IllegalArgumentException");
        } catch (IllegalArgumentException expected) {
        }
    }

    /**
     * Verify an IllegalArgumentException is thrown if callback is not provided.
     */
    @Test
    public void unregisterSoftApCallbackThrowsIllegalArgumentExceptionOnNullArgumentForCallback() {
        try {
            mWifiManager.unregisterSoftApCallback(null);
            fail("expected IllegalArgumentException");
        } catch (IllegalArgumentException expected) {
        }
    }

    /**
     * Verify main looper is used when handler is not provided.
     */
    @Test
    public void registerSoftApCallbackUsesMainLooperOnNullArgumentForHandler() {
        when(mContext.getMainLooper()).thenReturn(mLooper.getLooper());
        mWifiManager.registerSoftApCallback(mSoftApCallback, null);
        verify(mContext).getMainLooper();
    }

    /**
     * Verify the call to registerSoftApCallback goes to WifiServiceImpl.
     */
    @Test
    public void registerSoftApCallbackCallGoesToWifiServiceImpl() throws Exception {
        mWifiManager.registerSoftApCallback(mSoftApCallback, mHandler);
        verify(mWifiService).registerSoftApCallback(any(IBinder.class),
                any(ISoftApCallback.Stub.class), anyInt());
    }

    /**
     * Verify the call to unregisterSoftApCallback goes to WifiServiceImpl.
     */
    @Test
    public void unregisterSoftApCallbackCallGoesToWifiServiceImpl() throws Exception {
        ArgumentCaptor<Integer> callbackIdentifier = ArgumentCaptor.forClass(Integer.class);
        mWifiManager.registerSoftApCallback(mSoftApCallback, mHandler);
        verify(mWifiService).registerSoftApCallback(any(IBinder.class),
                any(ISoftApCallback.Stub.class), callbackIdentifier.capture());

        mWifiManager.unregisterSoftApCallback(mSoftApCallback);
        verify(mWifiService).unregisterSoftApCallback(eq((int) callbackIdentifier.getValue()));
    }

    /*
     * Verify client provided callback is being called through callback proxy
     */
    @Test
    public void softApCallbackProxyCallsOnStateChanged() throws Exception {
        ArgumentCaptor<ISoftApCallback.Stub> callbackCaptor =
                ArgumentCaptor.forClass(ISoftApCallback.Stub.class);
        mWifiManager.registerSoftApCallback(mSoftApCallback, mHandler);
        verify(mWifiService).registerSoftApCallback(any(IBinder.class), callbackCaptor.capture(),
                anyInt());

        callbackCaptor.getValue().onStateChanged(WIFI_AP_STATE_ENABLED, 0);
        mLooper.dispatchAll();
        verify(mSoftApCallback).onStateChanged(WIFI_AP_STATE_ENABLED, 0);
    }

    /*
     * Verify client provided callback is being called through callback proxy
     */
    @Test
    public void softApCallbackProxyCallsOnNumClientsChanged() throws Exception {
        ArgumentCaptor<ISoftApCallback.Stub> callbackCaptor =
                ArgumentCaptor.forClass(ISoftApCallback.Stub.class);
        mWifiManager.registerSoftApCallback(mSoftApCallback, mHandler);
        verify(mWifiService).registerSoftApCallback(any(IBinder.class), callbackCaptor.capture(),
                anyInt());

        final int testNumClients = 3;
        callbackCaptor.getValue().onNumClientsChanged(testNumClients);
        mLooper.dispatchAll();
        verify(mSoftApCallback).onNumClientsChanged(testNumClients);
    }

    /*
     * Verify client provided callback is being called through callback proxy on multiple events
     */
    @Test
    public void softApCallbackProxyCallsOnMultipleUpdates() throws Exception {
        ArgumentCaptor<ISoftApCallback.Stub> callbackCaptor =
                ArgumentCaptor.forClass(ISoftApCallback.Stub.class);
        mWifiManager.registerSoftApCallback(mSoftApCallback, mHandler);
        verify(mWifiService).registerSoftApCallback(any(IBinder.class), callbackCaptor.capture(),
                anyInt());

        final int testNumClients = 5;
        callbackCaptor.getValue().onStateChanged(WIFI_AP_STATE_ENABLING, 0);
        callbackCaptor.getValue().onNumClientsChanged(testNumClients);
        callbackCaptor.getValue().onStateChanged(WIFI_AP_STATE_FAILED, SAP_START_FAILURE_GENERAL);

        mLooper.dispatchAll();
        verify(mSoftApCallback).onStateChanged(WIFI_AP_STATE_ENABLING, 0);
        verify(mSoftApCallback).onNumClientsChanged(testNumClients);
        verify(mSoftApCallback).onStateChanged(WIFI_AP_STATE_FAILED, SAP_START_FAILURE_GENERAL);
    }

    /*
     * Verify client provided callback is being called on the correct thread
     */
    @Test
    public void softApCallbackIsCalledOnCorrectThread() throws Exception {
        ArgumentCaptor<ISoftApCallback.Stub> callbackCaptor =
                ArgumentCaptor.forClass(ISoftApCallback.Stub.class);
        TestLooper altLooper = new TestLooper();
        Handler altHandler = new Handler(altLooper.getLooper());
        mWifiManager.registerSoftApCallback(mSoftApCallback, altHandler);
        verify(mWifiService).registerSoftApCallback(any(IBinder.class), callbackCaptor.capture(),
                anyInt());

        callbackCaptor.getValue().onStateChanged(WIFI_AP_STATE_ENABLED, 0);
        altLooper.dispatchAll();
        verify(mSoftApCallback).onStateChanged(WIFI_AP_STATE_ENABLED, 0);
    }

    /**
     * Verify that the handler provided by the caller is used for registering soft AP callback.
     */
    @Test
    public void testCorrectLooperIsUsedForSoftApCallbackHandler() throws Exception {
        mWifiManager.registerSoftApCallback(mSoftApCallback, mHandler);
        mLooper.dispatchAll();
        verify(mWifiService).registerSoftApCallback(any(IBinder.class),
                any(ISoftApCallback.Stub.class), anyInt());
        verify(mContext, never()).getMainLooper();
    }

    /**
     * Verify that the handler provided by the caller is used for the observer.
     */