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

Commit e722cbd4 authored by Hai Shalom's avatar Hai Shalom
Browse files

[DPP R2] Added support for DPP R2 events

Added support for DPP R2 events that provide additional
details about the onboarding process of a remote enrollee.
Specifically, DPP R2 configurator waits for response from
the enrollee, which reports back the SSID, tried channels
and band support in case it cannot find the AP. When it
reports success, then it means that it is acutally connected.

Bug: 139381558
Test: Manual tests with DPP R1 and R2 enrollees
Test: atest DppManagerTest
Test: atest WifiManagerTest
Test: atest EasyConnectStatusCallbackTest
Test: act.py -c ../WifiDppConfig.json -tc WifiDppTest
Change-Id: I55a8b1e25737e90e304f1933d09ffcd6bcae6ef9
parent 1e89d231
Loading
Loading
Loading
Loading
+8 −1
Original line number Diff line number Diff line
@@ -5361,11 +5361,15 @@ package android.net.wifi {
    ctor public EasyConnectStatusCallback();
    method public abstract void onConfiguratorSuccess(int);
    method public abstract void onEnrolleeSuccess(int);
    method public abstract void onFailure(int);
    method public void onFailure(int);
    method public void onFailure(int, @Nullable String, @NonNull android.util.SparseArray<int[]>, @NonNull int[]);
    method public abstract void onProgress(int);
    field public static final int EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION = -2; // 0xfffffffe
    field public static final int EASY_CONNECT_EVENT_FAILURE_BUSY = -5; // 0xfffffffb
    field public static final int EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK = -10; // 0xfffffff6
    field public static final int EASY_CONNECT_EVENT_FAILURE_CONFIGURATION = -4; // 0xfffffffc
    field public static final int EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION = -11; // 0xfffffff5
    field public static final int EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION = -12; // 0xfffffff4
    field public static final int EASY_CONNECT_EVENT_FAILURE_GENERIC = -7; // 0xfffffff9
    field public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK = -9; // 0xfffffff7
    field public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_URI = -1; // 0xffffffff
@@ -5373,7 +5377,10 @@ package android.net.wifi {
    field public static final int EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED = -8; // 0xfffffff8
    field public static final int EASY_CONNECT_EVENT_FAILURE_TIMEOUT = -6; // 0xfffffffa
    field public static final int EASY_CONNECT_EVENT_PROGRESS_AUTHENTICATION_SUCCESS = 0; // 0x0
    field public static final int EASY_CONNECT_EVENT_PROGRESS_CONFIGURATION_ACCEPTED = 3; // 0x3
    field public static final int EASY_CONNECT_EVENT_PROGRESS_CONFIGURATION_SENT_WAITING_RESPONSE = 2; // 0x2
    field public static final int EASY_CONNECT_EVENT_PROGRESS_RESPONSE_PENDING = 1; // 0x1
    field public static final int EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_APPLIED = 1; // 0x1
    field public static final int EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT = 0; // 0x0
  }
+83 −19
Original line number Diff line number Diff line
@@ -17,32 +17,46 @@
package android.net.wifi;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Handler;
import android.util.SparseArray;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.Executor;

/**
 * Easy Connect (DPP) Status Callback. Use this callback to get status updates (success, failure,
 * progress) from the Easy Connect operation started with
 * {@link WifiManager#startEasyConnectAsConfiguratorInitiator(String,
 * int, int, Handler, EasyConnectStatusCallback)} or
 * {@link WifiManager#startEasyConnectAsEnrolleeInitiator(String,
 * Handler, EasyConnectStatusCallback)}
 * {@link WifiManager#startEasyConnectAsConfiguratorInitiator(String, int, int, Executor,
 * EasyConnectStatusCallback)} or {@link WifiManager#startEasyConnectAsEnrolleeInitiator(String,
 * Executor, EasyConnectStatusCallback)}
 *
 * @hide
 */
@SystemApi
public abstract class EasyConnectStatusCallback {
    /**
     * Easy Connect Success event: Configuration sent (Configurator mode).
     * Easy Connect R1 Success event: Configuration sent (Configurator mode). This is the last
     * and final Easy Connect event when either the local device or remote device implement R1.
     * If both devices implement R2, this event will never be received, and the
     * {@link EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_APPLIED} will be received.
     */
    public static final int EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT = 0;

    /**
     * East Connect R2 Success event: Configuration applied by Enrollee (Configurator mode).
     * This is the last and final Easy Connect event when both the local device and remote device
     * implement R2. If either the local device or remote device implement R1, this event will never
     * be received, and the {@link EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT} will be received.
     */
    public static final int EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_APPLIED = 1;

    /** @hide */
    @IntDef(prefix = {"EASY_CONNECT_EVENT_SUCCESS_"}, value = {
            EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT,
            EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_APPLIED,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface EasyConnectSuccessStatusCode {
@@ -58,10 +72,22 @@ public abstract class EasyConnectStatusCallback {
     */
    public static final int EASY_CONNECT_EVENT_PROGRESS_RESPONSE_PENDING = 1;

    /**
     * Easy Connect R2 Progress event: Configuration sent to Enrollee, waiting for response
     */
    public static final int EASY_CONNECT_EVENT_PROGRESS_CONFIGURATION_SENT_WAITING_RESPONSE = 2;

    /**
     * Easy Connect R2 Progress event: Configuration accepted by Enrollee, waiting for response
     */
    public static final int EASY_CONNECT_EVENT_PROGRESS_CONFIGURATION_ACCEPTED = 3;

    /** @hide */
    @IntDef(prefix = {"EASY_CONNECT_EVENT_PROGRESS_"}, value = {
            EASY_CONNECT_EVENT_PROGRESS_AUTHENTICATION_SUCCESS,
            EASY_CONNECT_EVENT_PROGRESS_RESPONSE_PENDING,
            EASY_CONNECT_EVENT_PROGRESS_CONFIGURATION_SENT_WAITING_RESPONSE,
            EASY_CONNECT_EVENT_PROGRESS_CONFIGURATION_ACCEPTED,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface EasyConnectProgressStatusCode {
@@ -114,6 +140,20 @@ public abstract class EasyConnectStatusCallback {
     */
    public static final int EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK = -9;

    /**
     * Easy Connect R2 Failure event: Enrollee cannot find the network.
     */
    public static final int EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK = -10;

    /**
     * Easy Connect R2 Failure event: Enrollee failed to authenticate with the network.
     */
    public static final int EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION = -11;

    /**
     * Easy Connect R2 Failure event: Enrollee rejected the configuration.
     */
    public static final int EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION = -12;

    /** @hide */
    @IntDef(prefix = {"EASY_CONNECT_EVENT_FAILURE_"}, value = {
@@ -126,6 +166,9 @@ public abstract class EasyConnectStatusCallback {
            EASY_CONNECT_EVENT_FAILURE_GENERIC,
            EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED,
            EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK,
            EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK,
            EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION,
            EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface EasyConnectFailureStatusCode {
@@ -138,8 +181,7 @@ public abstract class EasyConnectStatusCallback {
     * current Easy Connect
     * session, and no further callbacks will be called. This callback is the successful outcome
     * of a Easy Connect flow starting with
     * {@link WifiManager#startEasyConnectAsEnrolleeInitiator(String,
     * Handler,
     * {@link WifiManager#startEasyConnectAsEnrolleeInitiator(String, Executor,
     * EasyConnectStatusCallback)} .
     *
     * @param newNetworkId New Wi-Fi configuration with a network ID received from the configurator
@@ -148,13 +190,11 @@ public abstract class EasyConnectStatusCallback {

    /**
     * Called when a Easy Connect success event takes place, except for when configuration is
     * received from
     * an external Configurator. The callback onSuccessConfigReceived will be used in this case.
     * This callback marks the successful end of the current Easy Connect session, and no further
     * callbacks will be called. This callback is the successful outcome of a Easy Connect flow
     * starting with
     * {@link WifiManager#startEasyConnectAsConfiguratorInitiator(String, int, int, Handler,
     * EasyConnectStatusCallback)}.
     * received from an external Configurator. The callback onSuccessConfigReceived will be used in
     * this case. This callback marks the successful end of the current Easy Connect session, and no
     * further callbacks will be called. This callback is the successful outcome of a Easy Connect
     * flow starting with {@link WifiManager#startEasyConnectAsConfiguratorInitiator(String, int,
     * int, Executor,EasyConnectStatusCallback)}.
     *
     * @param code Easy Connect success status code.
     */
@@ -162,12 +202,36 @@ public abstract class EasyConnectStatusCallback {

    /**
     * Called when a Easy Connect Failure event takes place. This callback marks the unsuccessful
     * end of the
     * current Easy Connect session, and no further callbacks will be called.
     * end of the current Easy Connect session, and no further callbacks will be called.
     *
     * @param code Easy Connect failure status code.
     */
    public abstract void onFailure(@EasyConnectFailureStatusCode int code);
    public void onFailure(@EasyConnectFailureStatusCode int code) {}

    /**
     * Called when a Easy Connect Failure event takes place. This callback marks the unsuccessful
     * end of the current Easy Connect session, and no further callbacks will be called.
     *
     * Note: Easy Connect (DPP) R2, provides additional details for the Configurator when the
     * remote Enrollee is unable to connect to a network. The ssid, channelList and bandList
     * inputs are initialized only for the EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK failure
     * code, and the ssid and bandList are initialized for the
     * EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION failure code.
     *
     * @param code Easy Connect failure status code.
     * @param ssid SSID of the network the Enrollee tried to connect to.
     * @param channelListArray List of Global Operating classes and channel sets the Enrollee used
     *                         to scan to find the network, see the "DPP Connection Status Object"
     *                         section in the specification for the format, and Table E-4 in
     *                         IEEE Std 802.11-2016 - Global operating classes for more details.
     * @param operatingClassArray Array of bands the Enrollee supports as expressed as the Global
     *                            Operating Class, see Table E-4 in IEEE Std 802.11-2016 - Global
     *                            operating classes.
     */
    public void onFailure(@EasyConnectFailureStatusCode int code, @Nullable String ssid,
            @NonNull SparseArray<int[]> channelListArray, @NonNull int[] operatingClassArray) {
        onFailure(code);
    }

    /**
     * Called when Easy Connect events that indicate progress take place. Can be used by UI elements
+1 −1
Original line number Diff line number Diff line
@@ -38,7 +38,7 @@ oneway interface IDppCallback
    /**
     * Called when DPP Failure events take place.
     */
    void onFailure(int status);
    void onFailure(int status, String ssid, String channelList, in int[] bandArray);

    /**
     * Called when DPP events that indicate progress take place. Can be used by UI elements
+84 −2
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ import android.text.TextUtils;
import android.util.CloseGuard;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -70,6 +71,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.Executor;

/**
@@ -5177,6 +5179,7 @@ public class WifiManager {
        @Override
        public void onSuccessConfigReceived(int newNetworkId) {
            Log.d(TAG, "Easy Connect onSuccessConfigReceived callback");
            Binder.clearCallingIdentity();
            mExecutor.execute(() -> {
                mEasyConnectStatusCallback.onEnrolleeSuccess(newNetworkId);
            });
@@ -5185,22 +5188,28 @@ public class WifiManager {
        @Override
        public void onSuccess(int status) {
            Log.d(TAG, "Easy Connect onSuccess callback");
            Binder.clearCallingIdentity();
            mExecutor.execute(() -> {
                mEasyConnectStatusCallback.onConfiguratorSuccess(status);
            });
        }

        @Override
        public void onFailure(int status) {
        public void onFailure(int status, String ssid, String channelList,
                int[] operatingClassArray) {
            Log.d(TAG, "Easy Connect onFailure callback");
            Binder.clearCallingIdentity();
            mExecutor.execute(() -> {
                mEasyConnectStatusCallback.onFailure(status);
                SparseArray<int[]> channelListArray = parseDppChannelList(channelList);
                mEasyConnectStatusCallback.onFailure(status, ssid, channelListArray,
                        operatingClassArray);
            });
        }

        @Override
        public void onProgress(int status) {
            Log.d(TAG, "Easy Connect onProgress callback");
            Binder.clearCallingIdentity();
            mExecutor.execute(() -> {
                mEasyConnectStatusCallback.onProgress(status);
            });
@@ -5532,4 +5541,77 @@ public class WifiManager {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Parse the list of channels the DPP enrollee reports when it fails to find an AP.
     *
     * @param channelList List of channels in the format defined in the DPP specification.
     * @return A parsed sparse array, where the operating class is the key.
     * @hide
     */
    @VisibleForTesting
    public static SparseArray<int[]> parseDppChannelList(String channelList) {
        SparseArray<int[]> channelListArray = new SparseArray<>();

        if (TextUtils.isEmpty(channelList)) {
            return channelListArray;
        }
        StringTokenizer str = new StringTokenizer(channelList, ",");
        String classStr = null;
        List<Integer> channelsInClass = new ArrayList<>();

        try {
            while (str.hasMoreElements()) {
                String cur = str.nextToken();

                /**
                 * Example for a channel list:
                 *
                 * 81/1,2,3,4,5,6,7,8,9,10,11,115/36,40,44,48,118/52,56,60,64,121/100,104,108,112,
                 * 116,120,124,128,132,136,140,0/144,124/149,153,157,161,125/165
                 *
                 * Detect operating class by the delimiter of '/' and use a string tokenizer with
                 * ',' as a delimiter.
                 */
                int classDelim = cur.indexOf('/');
                if (classDelim != -1) {
                    if (classStr != null) {
                        // Store the last channel array in the sparse array, where the operating
                        // class is the key (as an integer).
                        int[] channelsArray = new int[channelsInClass.size()];
                        for (int i = 0; i < channelsInClass.size(); i++) {
                            channelsArray[i] = channelsInClass.get(i);
                        }
                        channelListArray.append(Integer.parseInt(classStr), channelsArray);
                        channelsInClass = new ArrayList<>();
                    }

                    // Init a new operating class and store the first channel
                    classStr = cur.substring(0, classDelim);
                    String channelStr = cur.substring(classDelim + 1);
                    channelsInClass.add(Integer.parseInt(channelStr));
                } else {
                    if (classStr == null) {
                        // Invalid format
                        Log.e(TAG, "Cannot parse DPP channel list");
                        return new SparseArray<>();
                    }
                    channelsInClass.add(Integer.parseInt(cur));
                }
            }

            // Store the last array
            if (classStr != null) {
                int[] channelsArray = new int[channelsInClass.size()];
                for (int i = 0; i < channelsInClass.size(); i++) {
                    channelsArray[i] = channelsInClass.get(i);
                }
                channelListArray.append(Integer.parseInt(classStr), channelsArray);
            }
            return channelListArray;
        } catch (NumberFormatException e) {
            Log.e(TAG, "Cannot parse DPP channel list");
            return new SparseArray<>();
        }
    }
}
+84 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import android.util.SparseArray;

import androidx.test.filters.SmallTest;

import org.junit.Before;
import org.junit.Test;

/**
 * Unit tests for {@link android.net.wifi.EasyConnectStatusCallbackTest}.
 */
@SmallTest
public class EasyConnectStatusCallbackTest {
    private EasyConnectStatusCallback mEasyConnectStatusCallback = new EasyConnectStatusCallback() {
        @Override
        public void onEnrolleeSuccess(int newNetworkId) {

        }

        @Override
        public void onConfiguratorSuccess(int code) {

        }

        @Override
        public void onProgress(int code) {

        }

        @Override
        public void onFailure(int code) {
            mOnFailureR1EventReceived = true;
            mLastCode = code;
        }
    };
    private boolean mOnFailureR1EventReceived;
    private int mLastCode;

    @Before
    public void setUp() {
        mOnFailureR1EventReceived = false;
        mLastCode = 0;
    }

    /**
     * Test that the legacy R1 onFailure is called by default if the R2 onFailure is not overridden
     * by the app.
     */
    @Test
    public void testR1OnFailureCalled() {

        SparseArray<int[]> channelList = new SparseArray<>();
        int[] channelArray = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};

        channelList.append(81, channelArray);
        mEasyConnectStatusCallback.onFailure(
                EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK,
                "SomeSSID", channelList, new int[] {81});

        assertTrue(mOnFailureR1EventReceived);
        assertEquals(mLastCode,
                EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK);
    }
}
Loading