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

Commit 841ea736 authored by Charlotte Lu's avatar Charlotte Lu Committed by Android (Google) Code Review
Browse files

Merge "Change WifiUtils.java into kotlin." into main

parents fe843f87 ef6c5ea0
Loading
Loading
Loading
Loading
+0 −447
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2017 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.settingslib.wifi;

import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLED;
import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.getMaxNetworkSelectionDisableReason;

import static com.android.settingslib.flags.Flags.newStatusBarIcons;

import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.icu.text.MessageFormat;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
import android.net.wifi.WifiInfo;
import android.net.wifi.sharedconnectivity.app.NetworkProviderInfo;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;

import androidx.annotation.VisibleForTesting;

import com.android.settingslib.R;

import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

public class WifiUtils {

    private static final String TAG = "WifiUtils";

    private static final int INVALID_RSSI = -127;

    /**
     * The intent action shows Wi-Fi dialog to connect Wi-Fi network.
     * <p>
     * Input: The calling package should put the chosen
     * com.android.wifitrackerlib.WifiEntry#getKey() to a string extra in the request bundle into
     * the {@link #EXTRA_CHOSEN_WIFI_ENTRY_KEY}.
     * <p>
     * Output: Nothing.
     */
    @VisibleForTesting
    static final String ACTION_WIFI_DIALOG = "com.android.settings.WIFI_DIALOG";

    /**
     * Specify a key that indicates the WifiEntry to be configured.
     */
    @VisibleForTesting
    static final String EXTRA_CHOSEN_WIFI_ENTRY_KEY = "key_chosen_wifientry_key";

    /**
     * The lookup key for a boolean that indicates whether a chosen WifiEntry request to connect to.
     * {@code true} means a chosen WifiEntry request to connect to.
     */
    @VisibleForTesting
    static final String EXTRA_CONNECT_FOR_CALLER = "connect_for_caller";

    /**
     * The intent action shows network details settings to allow configuration of Wi-Fi.
     * <p>
     * In some cases, a matching Activity may not exist, so ensure you
     * safeguard against this.
     * <p>
     * Input: The calling package should put the chosen
     * com.android.wifitrackerlib.WifiEntry#getKey() to a string extra in the request bundle into
     * the {@link #KEY_CHOSEN_WIFIENTRY_KEY}.
     * <p>
     * Output: Nothing.
     */
    public static final String ACTION_WIFI_DETAILS_SETTINGS =
            "android.settings.WIFI_DETAILS_SETTINGS";
    public static final String KEY_CHOSEN_WIFIENTRY_KEY = "key_chosen_wifientry_key";
    public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";

    static final int[] WIFI_PIE = getIconsBasedOnFlag();

    private static int[] getIconsBasedOnFlag() {
        if (newStatusBarIcons()) {
            return new int[] {
                    R.drawable.ic_wifi_0,
                    R.drawable.ic_wifi_1,
                    R.drawable.ic_wifi_2,
                    R.drawable.ic_wifi_3,
                    R.drawable.ic_wifi_4
            };
        } else {
            return new int[] {
                    com.android.internal.R.drawable.ic_wifi_signal_0,
                    com.android.internal.R.drawable.ic_wifi_signal_1,
                    com.android.internal.R.drawable.ic_wifi_signal_2,
                    com.android.internal.R.drawable.ic_wifi_signal_3,
                    com.android.internal.R.drawable.ic_wifi_signal_4
            };
        }
    }

    static final int[] NO_INTERNET_WIFI_PIE = getErrorIconsBasedOnFlag();

    private static int [] getErrorIconsBasedOnFlag() {
        if (newStatusBarIcons()) {
            return new int[] {
                    R.drawable.ic_wifi_0_error,
                    R.drawable.ic_wifi_1_error,
                    R.drawable.ic_wifi_2_error,
                    R.drawable.ic_wifi_3_error,
                    R.drawable.ic_wifi_4_error
            };
        } else {
            return new int[] {
                    R.drawable.ic_no_internet_wifi_signal_0,
                    R.drawable.ic_no_internet_wifi_signal_1,
                    R.drawable.ic_no_internet_wifi_signal_2,
                    R.drawable.ic_no_internet_wifi_signal_3,
                    R.drawable.ic_no_internet_wifi_signal_4
            };
        }
    }

    public static String buildLoggingSummary(AccessPoint accessPoint, WifiConfiguration config) {
        final StringBuilder summary = new StringBuilder();
        final WifiInfo info = accessPoint.getInfo();
        // Add RSSI/band information for this config, what was seen up to 6 seconds ago
        // verbose WiFi Logging is only turned on thru developers settings
        if (accessPoint.isActive() && info != null) {
            summary.append(" f=" + Integer.toString(info.getFrequency()));
        }
        summary.append(" " + getVisibilityStatus(accessPoint));
        if (config != null
                && (config.getNetworkSelectionStatus().getNetworkSelectionStatus()
                        != NETWORK_SELECTION_ENABLED)) {
            summary.append(" (" + config.getNetworkSelectionStatus().getNetworkStatusString());
            if (config.getNetworkSelectionStatus().getDisableTime() > 0) {
                long now = System.currentTimeMillis();
                long diff = (now - config.getNetworkSelectionStatus().getDisableTime()) / 1000;
                long sec = diff % 60; //seconds
                long min = (diff / 60) % 60; //minutes
                long hour = (min / 60) % 60; //hours
                summary.append(", ");
                if (hour > 0) summary.append(Long.toString(hour) + "h ");
                summary.append(Long.toString(min) + "m ");
                summary.append(Long.toString(sec) + "s ");
            }
            summary.append(")");
        }

        if (config != null) {
            NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
            for (int reason = 0; reason <= getMaxNetworkSelectionDisableReason(); reason++) {
                if (networkStatus.getDisableReasonCounter(reason) != 0) {
                    summary.append(" ")
                            .append(NetworkSelectionStatus
                                    .getNetworkSelectionDisableReasonString(reason))
                            .append("=")
                            .append(networkStatus.getDisableReasonCounter(reason));
                }
            }
        }

        return summary.toString();
    }

    /**
     * Returns the visibility status of the WifiConfiguration.
     *
     * @return autojoin debugging information
     * TODO: use a string formatter
     * ["rssi 5Ghz", "num results on 5GHz" / "rssi 5Ghz", "num results on 5GHz"]
     * For instance [-40,5/-30,2]
     */
    @VisibleForTesting
    static String getVisibilityStatus(AccessPoint accessPoint) {
        final WifiInfo info = accessPoint.getInfo();
        StringBuilder visibility = new StringBuilder();
        StringBuilder scans24GHz = new StringBuilder();
        StringBuilder scans5GHz = new StringBuilder();
        StringBuilder scans60GHz = new StringBuilder();
        String bssid = null;

        if (accessPoint.isActive() && info != null) {
            bssid = info.getBSSID();
            if (bssid != null) {
                visibility.append(" ").append(bssid);
            }
            visibility.append(" standard = ").append(info.getWifiStandard());
            visibility.append(" rssi=").append(info.getRssi());
            visibility.append(" ");
            visibility.append(" score=").append(info.getScore());
            if (accessPoint.getSpeed() != AccessPoint.Speed.NONE) {
                visibility.append(" speed=").append(accessPoint.getSpeedLabel());
            }
            visibility.append(String.format(" tx=%.1f,", info.getSuccessfulTxPacketsPerSecond()));
            visibility.append(String.format("%.1f,", info.getRetriedTxPacketsPerSecond()));
            visibility.append(String.format("%.1f ", info.getLostTxPacketsPerSecond()));
            visibility.append(String.format("rx=%.1f", info.getSuccessfulRxPacketsPerSecond()));
        }

        int maxRssi5 = INVALID_RSSI;
        int maxRssi24 = INVALID_RSSI;
        int maxRssi60 = INVALID_RSSI;
        final int maxDisplayedScans = 4;
        int num5 = 0; // number of scanned BSSID on 5GHz band
        int num24 = 0; // number of scanned BSSID on 2.4Ghz band
        int num60 = 0; // number of scanned BSSID on 60Ghz band
        int numBlockListed = 0;

        // TODO: sort list by RSSI or age
        long nowMs = SystemClock.elapsedRealtime();
        for (ScanResult result : accessPoint.getScanResults()) {
            if (result == null) {
                continue;
            }
            if (result.frequency >= AccessPoint.LOWER_FREQ_5GHZ
                    && result.frequency <= AccessPoint.HIGHER_FREQ_5GHZ) {
                // Strictly speaking: [4915, 5825]
                num5++;

                if (result.level > maxRssi5) {
                    maxRssi5 = result.level;
                }
                if (num5 <= maxDisplayedScans) {
                    scans5GHz.append(
                            verboseScanResultSummary(accessPoint, result, bssid,
                                    nowMs));
                }
            } else if (result.frequency >= AccessPoint.LOWER_FREQ_24GHZ
                    && result.frequency <= AccessPoint.HIGHER_FREQ_24GHZ) {
                // Strictly speaking: [2412, 2482]
                num24++;

                if (result.level > maxRssi24) {
                    maxRssi24 = result.level;
                }
                if (num24 <= maxDisplayedScans) {
                    scans24GHz.append(
                            verboseScanResultSummary(accessPoint, result, bssid,
                                    nowMs));
                }
            } else if (result.frequency >= AccessPoint.LOWER_FREQ_60GHZ
                    && result.frequency <= AccessPoint.HIGHER_FREQ_60GHZ) {
                // Strictly speaking: [60000, 61000]
                num60++;

                if (result.level > maxRssi60) {
                    maxRssi60 = result.level;
                }
                if (num60 <= maxDisplayedScans) {
                    scans60GHz.append(
                            verboseScanResultSummary(accessPoint, result, bssid,
                                    nowMs));
                }
            }
        }
        visibility.append(" [");
        if (num24 > 0) {
            visibility.append("(").append(num24).append(")");
            if (num24 > maxDisplayedScans) {
                visibility.append("max=").append(maxRssi24).append(",");
            }
            visibility.append(scans24GHz.toString());
        }
        visibility.append(";");
        if (num5 > 0) {
            visibility.append("(").append(num5).append(")");
            if (num5 > maxDisplayedScans) {
                visibility.append("max=").append(maxRssi5).append(",");
            }
            visibility.append(scans5GHz.toString());
        }
        visibility.append(";");
        if (num60 > 0) {
            visibility.append("(").append(num60).append(")");
            if (num60 > maxDisplayedScans) {
                visibility.append("max=").append(maxRssi60).append(",");
            }
            visibility.append(scans60GHz.toString());
        }
        if (numBlockListed > 0) {
            visibility.append("!").append(numBlockListed);
        }
        visibility.append("]");

        return visibility.toString();
    }

    @VisibleForTesting
    /* package */ static String verboseScanResultSummary(AccessPoint accessPoint, ScanResult result,
            String bssid, long nowMs) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(" \n{").append(result.BSSID);
        if (result.BSSID.equals(bssid)) {
            stringBuilder.append("*");
        }
        stringBuilder.append("=").append(result.frequency);
        stringBuilder.append(",").append(result.level);
        int speed = getSpecificApSpeed(result, accessPoint.getScoredNetworkCache());
        if (speed != AccessPoint.Speed.NONE) {
            stringBuilder.append(",")
                    .append(accessPoint.getSpeedLabel(speed));
        }
        int ageSeconds = (int) (nowMs - result.timestamp / 1000) / 1000;
        stringBuilder.append(",").append(ageSeconds).append("s");
        stringBuilder.append("}");
        return stringBuilder.toString();
    }

    @AccessPoint.Speed
    private static int getSpecificApSpeed(ScanResult result,
            Map<String, TimestampedScoredNetwork> scoredNetworkCache) {
        TimestampedScoredNetwork timedScore = scoredNetworkCache.get(result.BSSID);
        if (timedScore == null) {
            return AccessPoint.Speed.NONE;
        }
        // For debugging purposes we may want to use mRssi rather than result.level as the average
        // speed wil be determined by mRssi
        return timedScore.getScore().calculateBadge(result.level);
    }

    public static String getMeteredLabel(Context context, WifiConfiguration config) {
        // meteredOverride is whether the user manually set the metered setting or not.
        // meteredHint is whether the network itself is telling us that it is metered
        if (config.meteredOverride == WifiConfiguration.METERED_OVERRIDE_METERED
                || (config.meteredHint && !isMeteredOverridden(config))) {
            return context.getString(R.string.wifi_metered_label);
        }
        return context.getString(R.string.wifi_unmetered_label);
    }

    /**
     * Returns the Internet icon resource for a given RSSI level.
     *
     * @param level The number of bars to show (0-4)
     * @param noInternet True if a connected Wi-Fi network cannot access the Internet
     */
    public static int getInternetIconResource(int level, boolean noInternet) {
        int wifiLevel = level;
        if (wifiLevel < 0) {
            Log.e(TAG, "Wi-Fi level is out of range! level:" + level);
            wifiLevel = 0;
        } else if (level >= WIFI_PIE.length) {
            Log.e(TAG, "Wi-Fi level is out of range! level:" + level);
            wifiLevel = WIFI_PIE.length - 1;
        }
        return noInternet ? NO_INTERNET_WIFI_PIE[wifiLevel] : WIFI_PIE[wifiLevel];
    }

    /**
     * Returns the Hotspot network icon resource.
     *
     * @param deviceType The device type of Hotspot network
     */
    public static int getHotspotIconResource(int deviceType) {
        return switch (deviceType) {
            case NetworkProviderInfo.DEVICE_TYPE_PHONE -> R.drawable.ic_hotspot_phone;
            case NetworkProviderInfo.DEVICE_TYPE_TABLET -> R.drawable.ic_hotspot_tablet;
            case NetworkProviderInfo.DEVICE_TYPE_LAPTOP -> R.drawable.ic_hotspot_laptop;
            case NetworkProviderInfo.DEVICE_TYPE_WATCH -> R.drawable.ic_hotspot_watch;
            case NetworkProviderInfo.DEVICE_TYPE_AUTO -> R.drawable.ic_hotspot_auto;
            default -> R.drawable.ic_hotspot_phone;  // Return phone icon as default.
        };
    }

    /**
     * Wrapper the {@link #getInternetIconResource} for testing compatibility.
     */
    public static class InternetIconInjector {

        protected final Context mContext;

        public InternetIconInjector(Context context) {
            mContext = context;
        }

        /**
         * Returns the Internet icon for a given RSSI level.
         *
         * @param noInternet True if a connected Wi-Fi network cannot access the Internet
         * @param level The number of bars to show (0-4)
         */
        public Drawable getIcon(boolean noInternet, int level) {
            return mContext.getDrawable(WifiUtils.getInternetIconResource(level, noInternet));
        }
    }

    public static boolean isMeteredOverridden(WifiConfiguration config) {
        return config.meteredOverride != WifiConfiguration.METERED_OVERRIDE_NONE;
    }

    /**
     * Returns the Intent for Wi-Fi dialog.
     *
     * @param key              The Wi-Fi entry key
     * @param connectForCaller True if a chosen WifiEntry request to connect to
     */
    public static Intent getWifiDialogIntent(String key, boolean connectForCaller) {
        final Intent intent = new Intent(ACTION_WIFI_DIALOG);
        intent.putExtra(EXTRA_CHOSEN_WIFI_ENTRY_KEY, key);
        intent.putExtra(EXTRA_CONNECT_FOR_CALLER, connectForCaller);
        return intent;
    }

    /**
     * Returns the Intent for Wi-Fi network details settings.
     *
     * @param key The Wi-Fi entry key
     */
    public static Intent getWifiDetailsSettingsIntent(String key) {
        final Intent intent = new Intent(ACTION_WIFI_DETAILS_SETTINGS);
        final Bundle bundle = new Bundle();
        bundle.putString(KEY_CHOSEN_WIFIENTRY_KEY, key);
        intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, bundle);
        return intent;
    }

    /**
     * Returns the string of Wi-Fi tethering summary for connected devices.
     *
     * @param context          The application context
     * @param connectedDevices The count of connected devices
     */
    public static String getWifiTetherSummaryForConnectedDevices(Context context,
            int connectedDevices) {
        MessageFormat msgFormat = new MessageFormat(
                context.getResources().getString(R.string.wifi_tether_connected_summary),
                Locale.getDefault());
        Map<String, Object> arguments = new HashMap<>();
        arguments.put("count", connectedDevices);
        return msgFormat.format(arguments);
    }
}
+462 −0

File added.

Preview size limit exceeded, changes collapsed.