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

Commit ace322e4 authored by David Su's avatar David Su
Browse files

Expose additional WifiScanner @SystemApis

Exposed scan type, HiddenNetwork, and
register/unregisterScanListener().

Bug: 143614759
Test: atest FrameworkWifiApiTests
Change-Id: Ie926dc8d75266dad13e5d1589f8e14011a856a01
parent 58bea80f
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -5405,6 +5405,7 @@ package android.net.wifi {
    method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public java.util.List<java.lang.Integer> getAvailableChannels(int);
    method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean getScanResults();
    method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public java.util.List<android.net.wifi.ScanResult> getSingleScanResults();
    method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void registerScanListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiScanner.ScanListener);
    method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setScanningEnabled(boolean);
    method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startBackgroundScan(android.net.wifi.WifiScanner.ScanSettings, android.net.wifi.WifiScanner.ScanListener);
    method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startBackgroundScan(android.net.wifi.WifiScanner.ScanSettings, android.net.wifi.WifiScanner.ScanListener, android.os.WorkSource);
@@ -5416,6 +5417,7 @@ package android.net.wifi {
    method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void stopScan(android.net.wifi.WifiScanner.ScanListener);
    method @Deprecated public void stopTrackingBssids(android.net.wifi.WifiScanner.BssidListener);
    method @Deprecated public void stopTrackingWifiChange(android.net.wifi.WifiScanner.WifiChangeListener);
    method public void unregisterScanListener(@NonNull android.net.wifi.WifiScanner.ScanListener);
    field public static final int MAX_SCAN_PERIOD_MS = 1024000; // 0xfa000
    field public static final int MIN_SCAN_PERIOD_MS = 1000; // 0x3e8
    field public static final int REASON_DUPLICATE_REQEUST = -5; // 0xfffffffb
@@ -5428,6 +5430,9 @@ package android.net.wifi {
    field public static final int REPORT_EVENT_AFTER_EACH_SCAN = 1; // 0x1
    field public static final int REPORT_EVENT_FULL_SCAN_RESULT = 2; // 0x2
    field public static final int REPORT_EVENT_NO_BATCH = 4; // 0x4
    field public static final int SCAN_TYPE_HIGH_ACCURACY = 2; // 0x2
    field public static final int SCAN_TYPE_LOW_LATENCY = 0; // 0x0
    field public static final int SCAN_TYPE_LOW_POWER = 1; // 0x1
    field public static final int WIFI_BAND_24_GHZ = 1; // 0x1
    field public static final int WIFI_BAND_5_GHZ = 2; // 0x2
    field public static final int WIFI_BAND_5_GHZ_DFS_ONLY = 4; // 0x4
@@ -5496,6 +5501,7 @@ package android.net.wifi {
    ctor public WifiScanner.ScanSettings();
    field public int band;
    field public android.net.wifi.WifiScanner.ChannelSpec[] channels;
    field @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public final java.util.List<android.net.wifi.WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworks;
    field public boolean hideFromAppOps;
    field public boolean ignoreLocationSettings;
    field public int maxPeriodInMs;
@@ -5504,6 +5510,12 @@ package android.net.wifi {
    field public int periodInMs;
    field public int reportEvents;
    field public int stepCount;
    field @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public int type;
  }
  public static class WifiScanner.ScanSettings.HiddenNetwork {
    ctor public WifiScanner.ScanSettings.HiddenNetwork(@NonNull String);
    field @NonNull public final String ssid;
  }
  @Deprecated public static interface WifiScanner.WifiChangeListener extends android.net.wifi.WifiScanner.ActionListener {
+3 −1
Original line number Diff line number Diff line
@@ -181,6 +181,8 @@ MutableBareField: android.net.wifi.WifiConfiguration#saePasswordId:
    Bare field saePasswordId must be marked final, or moved behind accessors if mutable
MutableBareField: android.net.wifi.WifiConfiguration#shared:
    Bare field shared must be marked final, or moved behind accessors if mutable
MutableBareField: android.net.wifi.WifiScanner.ScanSettings#type:
    Bare field type must be marked final, or moved behind accessors if mutable


NoClone: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #0:
+29 −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 java.util.concurrent.Executor;

/**
 * An executor implementation that runs synchronously on the current thread.
 * @hide
 */
public class SynchronousExecutor implements Executor {
    @Override
    public void execute(Runnable r) {
        r.run();
    }
}
+141 −80
Original line number Diff line number Diff line
@@ -17,13 +17,16 @@
package android.net.wifi;

import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -45,6 +48,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;

/**
 * This class provides a way to scan the Wifi universe around the device
@@ -196,24 +200,29 @@ public class WifiScanner {
     */
    public static final int REPORT_EVENT_NO_BATCH = (1 << 2);

    /** @hide */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = {"SCAN_TYPE_"}, value = {
            SCAN_TYPE_LOW_LATENCY,
            SCAN_TYPE_LOW_POWER,
            SCAN_TYPE_HIGH_ACCURACY})
    public @interface ScanType {}

    /**
     * This is used to indicate the purpose of the scan to the wifi chip in
     * {@link ScanSettings#type}.
     * On devices with multiple hardware radio chains (and hence different modes of scan),
     * this type serves as an indication to the hardware on what mode of scan to perform.
     * Only apps holding android.Manifest.permission.NETWORK_STACK permission can set this value.
     *
     * Note: This serves as an intent and not as a stipulation, the wifi chip
     * might honor or ignore the indication based on the current radio conditions. Always
     * use the {@link ScanResult#radioChainInfos} to figure out the radio chain configuration used
     * to receive the corresponding scan result.
     * Optimize the scan for lower latency.
     * @see ScanSettings#type
     */
    /** {@hide} */
    public static final int TYPE_LOW_LATENCY = 0;
    /** {@hide} */
    public static final int TYPE_LOW_POWER = 1;
    /** {@hide} */
    public static final int TYPE_HIGH_ACCURACY = 2;
    public static final int SCAN_TYPE_LOW_LATENCY = 0;
    /**
     * Optimize the scan for lower power usage.
     * @see ScanSettings#type
     */
    public static final int SCAN_TYPE_LOW_POWER = 1;
    /**
     * Optimize the scan for higher accuracy.
     * @see ScanSettings#type
     */
    public static final int SCAN_TYPE_HIGH_ACCURACY = 2;

    /** {@hide} */
    public static final String SCAN_PARAMS_SCAN_SETTINGS_KEY = "ScanSettings";
@@ -228,18 +237,14 @@ public class WifiScanner {
     * scan configuration parameters to be sent to {@link #startBackgroundScan}
     */
    public static class ScanSettings implements Parcelable {
        /**
         * Hidden network to be scanned for.
         * {@hide}
         */
        /** Hidden network to be scanned for. */
        public static class HiddenNetwork {
            /** SSID of the network */
            public String ssid;
            @NonNull
            public final String ssid;

            /**
             * Default constructor for HiddenNetwork.
             */
            public HiddenNetwork(String ssid) {
            /** Default constructor for HiddenNetwork. */
            public HiddenNetwork(@NonNull String ssid) {
                this.ssid = ssid;
            }
        }
@@ -249,12 +254,12 @@ public class WifiScanner {
        /** list of channels; used when band is set to WIFI_BAND_UNSPECIFIED */
        public ChannelSpec[] channels;
        /**
         * list of hidden networks to scan for. Explicit probe requests are sent out for such
         * List of hidden networks to scan for. Explicit probe requests are sent out for such
         * networks during scan. Only valid for single scan requests.
         * {@hide}
         */
        @NonNull
        @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
        public HiddenNetwork[] hiddenNetworks;
        public final List<HiddenNetwork> hiddenNetworks = new ArrayList<>();
        /** period of background scan; in millisecond, 0 => single shot scan */
        public int periodInMs;
        /** must have a valid REPORT_EVENT value */
@@ -285,11 +290,24 @@ public class WifiScanner {
        public boolean isPnoScan;
        /**
         * Indicate the type of scan to be performed by the wifi chip.
         * Default value: {@link #TYPE_LOW_LATENCY}.
         * {@hide}
         *
         * On devices with multiple hardware radio chains (and hence different modes of scan),
         * this type serves as an indication to the hardware on what mode of scan to perform.
         * Only apps holding {@link android.Manifest.permission.NETWORK_STACK} permission can set
         * this value.
         *
         * Note: This serves as an intent and not as a stipulation, the wifi chip
         * might honor or ignore the indication based on the current radio conditions. Always
         * use the {@link ScanResult#radioChainInfos} to figure out the radio chain configuration
         * used to receive the corresponding scan result.
         *
         * One of {@link #SCAN_TYPE_LOW_LATENCY}, {@link #SCAN_TYPE_LOW_POWER},
         * {@link #SCAN_TYPE_HIGH_ACCURACY}.
         * Default value: {@link #SCAN_TYPE_LOW_LATENCY}.
         */
        @ScanType
        @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
        public int type = TYPE_LOW_LATENCY;
        public int type = SCAN_TYPE_LOW_LATENCY;
        /**
         * This scan request may ignore location settings while receiving scans. This should only
         * be used in emergency situations.
@@ -336,13 +354,9 @@ public class WifiScanner {
            } else {
                dest.writeInt(0);
            }
            if (hiddenNetworks != null) {
                dest.writeInt(hiddenNetworks.length);
                for (int i = 0; i < hiddenNetworks.length; i++) {
                    dest.writeString(hiddenNetworks[i].ssid);
                }
            } else {
                dest.writeInt(0);
            dest.writeInt(hiddenNetworks.size());
            for (HiddenNetwork hiddenNetwork : hiddenNetworks) {
                dest.writeString(hiddenNetwork.ssid);
            }
        }

@@ -372,10 +386,10 @@ public class WifiScanner {
                            settings.channels[i] = spec;
                        }
                        int numNetworks = in.readInt();
                        settings.hiddenNetworks = new HiddenNetwork[numNetworks];
                        settings.hiddenNetworks.clear();
                        for (int i = 0; i < numNetworks; i++) {
                            String ssid = in.readString();
                            settings.hiddenNetworks[i] = new HiddenNetwork(ssid);;
                            settings.hiddenNetworks.add(new HiddenNetwork(ssid));
                        }
                        return settings;
                    }
@@ -801,33 +815,44 @@ public class WifiScanner {
    }

    /**
     * Register a listener that will receive results from all single scans
     * Either the onSuccess/onFailure will be called once when the listener is registered. After
     * (assuming onSuccess was called) all subsequent single scan results will be delivered to the
     * listener. It is possible that onFullResult will not be called for all results of the first
     * scan if the listener was registered during the scan.
     * Register a listener that will receive results from all single scans.
     * Either the {@link ScanListener#onSuccess()} or  {@link ScanListener#onFailure(int, String)}
     * method will be called once when the listener is registered.
     * Afterwards (assuming onSuccess was called), all subsequent single scan results will be
     * delivered to the listener. It is possible that onFullResult will not be called for all
     * results of the first scan if the listener was registered during the scan.
     *
     * @param listener specifies the object to report events to. This object is also treated as a
     *                 key for this request, and must also be specified to cancel the request.
     *                 Multiple requests should also not share this object.
     * {@hide}
     */
    @RequiresPermission(Manifest.permission.NETWORK_STACK)
    public void registerScanListener(ScanListener listener) {
    public void registerScanListener(@NonNull @CallbackExecutor Executor executor,
            @NonNull ScanListener listener) {
        Preconditions.checkNotNull(executor, "executor cannot be null");
        Preconditions.checkNotNull(listener, "listener cannot be null");
        int key = addListener(listener);
        int key = addListener(listener, executor);
        if (key == INVALID_KEY) return;
        validateChannel();
        mAsyncChannel.sendMessage(CMD_REGISTER_SCAN_LISTENER, 0, key);
    }

    /**
     * Overload of {@link #registerScanListener(Executor, ScanListener)} that executes the callback
     * synchronously.
     * @hide
     */
    @RequiresPermission(Manifest.permission.NETWORK_STACK)
    public void registerScanListener(@NonNull ScanListener listener) {
        registerScanListener(new SynchronousExecutor(), listener);
    }

    /**
     * Deregister a listener for ongoing single scans
     * @param listener specifies which scan to cancel; must be same object as passed in {@link
     *  #registerScanListener}
     * {@hide}
     */
    public void deregisterScanListener(ScanListener listener) {
    public void unregisterScanListener(@NonNull ScanListener listener) {
        Preconditions.checkNotNull(listener, "listener cannot be null");
        int key = removeListener(listener);
        if (key == INVALID_KEY) return;
@@ -1280,6 +1305,7 @@ public class WifiScanner {
    private int mListenerKey = 1;

    private final SparseArray mListenerMap = new SparseArray();
    private final SparseArray<Executor> mExecutorMap = new SparseArray<>();
    private final Object mListenerMapLock = new Object();

    private AsyncChannel mAsyncChannel;
@@ -1327,10 +1353,14 @@ public class WifiScanner {
                "No permission to access and change wifi or a bad initialization");
    }

    private int addListener(ActionListener listener) {
        return addListener(listener, null);
    }

    // Add a listener into listener map. If the listener already exists, return INVALID_KEY and
    // send an error message to internal handler; Otherwise add the listener to the listener map and
    // return the key of the listener.
    private int addListener(ActionListener listener) {
    private int addListener(ActionListener listener, Executor executor) {
        synchronized (mListenerMapLock) {
            boolean keyExists = (getListenerKey(listener) != INVALID_KEY);
            // Note we need to put the listener into listener map even if it's a duplicate as the
@@ -1346,6 +1376,7 @@ public class WifiScanner {
                message.sendToTarget();
                return INVALID_KEY;
            } else {
                mExecutorMap.put(key, executor);
                return key;
            }
        }
@@ -1363,11 +1394,22 @@ public class WifiScanner {
        return key;
    }

    private Object getListener(int key) {
        if (key == INVALID_KEY) return null;
    private static class ListenerWithExecutor {
        @Nullable final Object mListener;
        @Nullable final Executor mExecutor;

        ListenerWithExecutor(@Nullable Object listener, @Nullable Executor executor) {
            mListener = listener;
            mExecutor = executor;
        }
    }

    private ListenerWithExecutor getListenerWithExecutor(int key) {
        if (key == INVALID_KEY) return new ListenerWithExecutor(null, null);
        synchronized (mListenerMapLock) {
            Object listener = mListenerMap.get(key);
            return listener;
            Executor executor = mExecutorMap.get(key);
            return new ListenerWithExecutor(listener, executor);
        }
    }

@@ -1388,6 +1430,7 @@ public class WifiScanner {
        synchronized (mListenerMapLock) {
            Object listener = mListenerMap.get(key);
            mListenerMap.remove(key);
            mExecutorMap.remove(key);
            return listener;
        }
    }
@@ -1400,6 +1443,7 @@ public class WifiScanner {
        }
        synchronized (mListenerMapLock) {
            mListenerMap.remove(key);
            mExecutorMap.remove(key);
            return key;
        }
    }
@@ -1458,7 +1502,8 @@ public class WifiScanner {
                    return;
            }

            Object listener = getListener(msg.arg2);
            ListenerWithExecutor listenerWithExecutor = getListenerWithExecutor(msg.arg2);
            Object listener = listenerWithExecutor.mListener;

            if (listener == null) {
                if (DBG) Log.d(TAG, "invalid listener key = " + msg.arg2);
@@ -1467,36 +1512,52 @@ public class WifiScanner {
                if (DBG) Log.d(TAG, "listener key = " + msg.arg2);
            }

            Executor executor = listenerWithExecutor.mExecutor;
            if (executor == null) {
                executor = new SynchronousExecutor();
            }

            switch (msg.what) {
                /* ActionListeners grouped together */
                case CMD_OP_SUCCEEDED :
                    ((ActionListener) listener).onSuccess();
                    break;
                case CMD_OP_SUCCEEDED: {
                    ActionListener actionListener = (ActionListener) listener;
                    Binder.clearCallingIdentity();
                    executor.execute(actionListener::onSuccess);
                } break;
                case CMD_OP_FAILED: {
                    OperationResult result = (OperationResult) msg.obj;
                        ((ActionListener) listener).onFailure(result.reason, result.description);
                    ActionListener actionListener = (ActionListener) listener;
                    removeListener(msg.arg2);
                    }
                    break;
                case CMD_SCAN_RESULT :
                    ((ScanListener) listener).onResults(
                            ((ParcelableScanData) msg.obj).getResults());
                    return;
                case CMD_FULL_SCAN_RESULT :
                    Binder.clearCallingIdentity();
                    executor.execute(() ->
                            actionListener.onFailure(result.reason, result.description));
                } break;
                case CMD_SCAN_RESULT: {
                    ScanListener scanListener = (ScanListener) listener;
                    ParcelableScanData parcelableScanData = (ParcelableScanData) msg.obj;
                    Binder.clearCallingIdentity();
                    executor.execute(() -> scanListener.onResults(parcelableScanData.getResults()));
                } break;
                case CMD_FULL_SCAN_RESULT: {
                    ScanResult result = (ScanResult) msg.obj;
                    ((ScanListener) listener).onFullResult(result);
                    return;
                case CMD_SINGLE_SCAN_COMPLETED:
                    ScanListener scanListener = ((ScanListener) listener);
                    Binder.clearCallingIdentity();
                    executor.execute(() -> scanListener.onFullResult(result));
                } break;
                case CMD_SINGLE_SCAN_COMPLETED: {
                    if (DBG) Log.d(TAG, "removing listener for single scan");
                    removeListener(msg.arg2);
                    break;
                case CMD_PNO_NETWORK_FOUND:
                    ((PnoScanListener) listener).onPnoNetworkFound(
                            ((ParcelableScanResults) msg.obj).getResults());
                    return;
                default:
                } break;
                case CMD_PNO_NETWORK_FOUND: {
                    PnoScanListener pnoScanListener = (PnoScanListener) listener;
                    ParcelableScanResults parcelableScanResults = (ParcelableScanResults) msg.obj;
                    Binder.clearCallingIdentity();
                    executor.execute(() ->
                            pnoScanListener.onPnoNetworkFound(parcelableScanResults.getResults()));
                } break;
                default: {
                    if (DBG) Log.d(TAG, "Ignoring message " + msg.what);
                    return;
                } break;
            }
        }
    }
+0 −9
Original line number Diff line number Diff line
@@ -1436,15 +1436,6 @@ public class WifiManagerTest {
                eq((int) listenerIdentifier.getValue()));
    }

    /**
     * Defined for testing purpose.
     */
    class SynchronousExecutor implements Executor {
        public void execute(Runnable r) {
            r.run();
        }
    }

    /**
     * Test behavior of isEnhancedOpenSupported
     */
Loading