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

Commit c0fc2769 authored by Chalard Jean's avatar Chalard Jean Committed by Paul Hu
Browse files

[TNU06] Add roaming notification

Warn user of potential data charges if the backhaul is
cellular and user is roaming.

Bug: 145629001
Test: atest TetheringTests
Change-Id: I74b4f87c2f6aad09e05d3f2a779f880396885953
Merged-In: I74b4f87c2f6aad09e05d3f2a779f880396885953
(cherry picked from commit 1af69e5b, aosp/1237026)
parent e5eb16d8
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -17,4 +17,7 @@
    <!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to
         "0" for disable this feature. -->
    <integer name="delay_to_show_no_upstream_after_no_backhaul">5000</integer>

    <!-- Config for showing upstream roaming notification. -->
    <bool name="config_upstream_roaming_notification">true</bool>
</resources>
 No newline at end of file
+3 −0
Original line number Diff line number Diff line
@@ -17,4 +17,7 @@
    <!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to
         "0" for disable this feature. -->
    <integer name="delay_to_show_no_upstream_after_no_backhaul">5000</integer>

    <!-- Config for showing upstream roaming notification. -->
    <bool name="config_upstream_roaming_notification">true</bool>
</resources>
 No newline at end of file
+5 −0
Original line number Diff line number Diff line
@@ -208,4 +208,9 @@
    <!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to
         "-1" for disable this feature. -->
    <integer name="delay_to_show_no_upstream_after_no_backhaul">-1</integer>

    <!-- Cellular roaming notification is shown when upstream is cellular network and in roaming
         state. -->
    <!-- Config for showing upstream roaming notification. -->
    <bool name="config_upstream_roaming_notification">false</bool>
</resources>
+13 −4
Original line number Diff line number Diff line
@@ -81,6 +81,7 @@ import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.TetherStatesParcel;
import android.net.TetheredClient;
@@ -1476,7 +1477,7 @@ public class Tethering {
            if (mTetherUpstream != newUpstream) {
                mTetherUpstream = newUpstream;
                mUpstreamNetworkMonitor.setCurrentUpstream(mTetherUpstream);
                reportUpstreamChanged(mTetherUpstream);
                reportUpstreamChanged(ns);
            }
        }

@@ -1598,7 +1599,8 @@ public class Tethering {
            }
        }

        private void handleUpstreamNetworkMonitorCallback(int arg1, Object o) {
        @VisibleForTesting
        void handleUpstreamNetworkMonitorCallback(int arg1, Object o) {
            if (arg1 == UpstreamNetworkMonitor.NOTIFY_LOCAL_PREFIXES) {
                mOffload.sendOffloadExemptPrefixes((Set<IpPrefix>) o);
                return;
@@ -1624,6 +1626,9 @@ public class Tethering {

            switch (arg1) {
                case UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES:
                    if (ns.network.equals(mTetherUpstream)) {
                        mNotificationUpdater.onUpstreamCapabilitiesChanged(ns.networkCapabilities);
                    }
                    handleNewUpstreamNetworkState(ns);
                    break;
                case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES:
@@ -2009,8 +2014,10 @@ public class Tethering {
        });
    }

    private void reportUpstreamChanged(Network network) {
    private void reportUpstreamChanged(UpstreamNetworkState ns) {
        final int length = mTetheringEventCallbacks.beginBroadcast();
        final Network network = (ns != null) ? ns.network : null;
        final NetworkCapabilities capabilities = (ns != null) ? ns.networkCapabilities : null;
        try {
            for (int i = 0; i < length; i++) {
                try {
@@ -2022,7 +2029,9 @@ public class Tethering {
        } finally {
            mTetheringEventCallbacks.finishBroadcast();
        }
        mNotificationUpdater.onUpstreamNetworkChanged(network);
        // Need to notify capabilities change after upstream network changed because new network's
        // capabilities should be checked every time.
        mNotificationUpdater.onUpstreamCapabilitiesChanged(capabilities);
    }

    private void reportConfigurationChanged(TetheringConfigurationParcel config) {
+80 −22
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.networkstack.tethering;

import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
import static android.net.TetheringManager.TETHERING_USB;
import static android.net.TetheringManager.TETHERING_WIFI;
@@ -30,7 +31,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -50,6 +51,9 @@ import androidx.annotation.Nullable;

import com.android.internal.annotations.VisibleForTesting;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * A class to display tethering-related notifications.
 *
@@ -82,6 +86,9 @@ public class TetheringNotificationUpdater {
    // Id to update and cancel no upstream notification. Must be unique within the tethering app.
    @VisibleForTesting
    static final int NO_UPSTREAM_NOTIFICATION_ID = 1002;
    // Id to update and cancel roaming notification. Must be unique within the tethering app.
    @VisibleForTesting
    static final int ROAMING_NOTIFICATION_ID = 1003;
    @VisibleForTesting
    static final int NO_ICON_ID = 0;
    @VisibleForTesting
@@ -95,13 +102,14 @@ public class TetheringNotificationUpdater {
    private final Handler mHandler;

    // WARNING : the constructor is called on a different thread. Thread safety therefore
    // relies on these values being initialized to 0 or false, and not any other value. If you need
    // to change this, you will need to change the thread where the constructor is invoked,
    // or to introduce synchronization.
    // relies on these values being initialized to 0, false or null, and not any other value. If you
    // need to change this, you will need to change the thread where the constructor is invoked, or
    // to introduce synchronization.
    // Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2.
    // This value has to be made 1 2 and 4, and OR'd with the others.
    private int mDownstreamTypesMask = DOWNSTREAM_NONE;
    private boolean mNoUpstream = false;
    private boolean mRoaming = false;

    // WARNING : this value is not able to being initialized to 0 and must have volatile because
    // telephony service is not guaranteed that is up before tethering service starts. If telephony
@@ -110,7 +118,13 @@ public class TetheringNotificationUpdater {
    // INVALID_SUBSCRIPTION_ID.
    private volatile int mActiveDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;

    @IntDef({ENABLE_NOTIFICATION_ID, RESTRICTED_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID})
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(value = {
            ENABLE_NOTIFICATION_ID,
            RESTRICTED_NOTIFICATION_ID,
            NO_UPSTREAM_NOTIFICATION_ID,
            ROAMING_NOTIFICATION_ID
    })
    @interface NotificationId {}

    private static final class MccMncOverrideInfo {
@@ -160,26 +174,22 @@ public class TetheringNotificationUpdater {

    /** Called when downstream has changed */
    public void onDownstreamChanged(@IntRange(from = 0, to = 7) final int downstreamTypesMask) {
        if (mDownstreamTypesMask == downstreamTypesMask) return;
        mDownstreamTypesMask = downstreamTypesMask;
        updateEnableNotification();
        updateNoUpstreamNotification();
        updateActiveNotifications(
                mActiveDataSubId, downstreamTypesMask, mNoUpstream, mRoaming);
    }

    /** Called when active data subscription id changed */
    public void onActiveDataSubscriptionIdChanged(final int subId) {
        if (mActiveDataSubId == subId) return;
        mActiveDataSubId = subId;
        updateEnableNotification();
        updateNoUpstreamNotification();
        updateActiveNotifications(subId, mDownstreamTypesMask, mNoUpstream, mRoaming);
    }

    /** Called when upstream network changed */
    public void onUpstreamNetworkChanged(@Nullable final Network network) {
        final boolean isNoUpstream = (network == null);
        if (mNoUpstream == isNoUpstream) return;
        mNoUpstream = isNoUpstream;
        updateNoUpstreamNotification();
    /** Called when upstream network capabilities changed */
    public void onUpstreamCapabilitiesChanged(@Nullable final NetworkCapabilities capabilities) {
        final boolean isNoUpstream = (capabilities == null);
        final boolean isRoaming = capabilities != null
                && !capabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING);
        updateActiveNotifications(
                mActiveDataSubId, mDownstreamTypesMask, isNoUpstream, isRoaming);
    }

    @NonNull
@@ -208,6 +218,25 @@ public class TetheringNotificationUpdater {
        return res;
    }

    private void updateActiveNotifications(final int subId, final int downstreamTypes,
            final boolean noUpstream, final boolean isRoaming) {
        final boolean tetheringActiveChanged =
                (downstreamTypes == DOWNSTREAM_NONE) != (mDownstreamTypesMask == DOWNSTREAM_NONE);
        final boolean subIdChanged = subId != mActiveDataSubId;
        final boolean downstreamChanged = downstreamTypes != mDownstreamTypesMask;
        final boolean upstreamChanged = noUpstream != mNoUpstream;
        final boolean roamingChanged = isRoaming != mRoaming;
        final boolean updateAll = tetheringActiveChanged || subIdChanged;
        mActiveDataSubId = subId;
        mDownstreamTypesMask = downstreamTypes;
        mNoUpstream = noUpstream;
        mRoaming = isRoaming;

        if (updateAll || downstreamChanged) updateEnableNotification();
        if (updateAll || upstreamChanged) updateNoUpstreamNotification();
        if (updateAll || roamingChanged) updateRoamingNotification();
    }

    private void updateEnableNotification() {
        final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE;

@@ -219,14 +248,20 @@ public class TetheringNotificationUpdater {
    private void updateNoUpstreamNotification() {
        final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE;

        if (tetheringInactive
                || !mNoUpstream
                || setupNoUpstreamNotification() == NO_NOTIFY) {
        if (tetheringInactive || !mNoUpstream || setupNoUpstreamNotification() == NO_NOTIFY) {
            clearNotification(NO_UPSTREAM_NOTIFICATION_ID);
            mHandler.removeMessages(EVENT_SHOW_NO_UPSTREAM);
        }
    }

    private void updateRoamingNotification() {
        final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE;

        if (tetheringInactive || !mRoaming || setupRoamingNotification() == NO_NOTIFY) {
            clearNotification(ROAMING_NOTIFICATION_ID);
        }
    }

    @VisibleForTesting
    void tetheringRestrictionLifted() {
        clearNotification(RESTRICTED_NOTIFICATION_ID);
@@ -333,6 +368,29 @@ public class TetheringNotificationUpdater {
        return icons;
    }

    private boolean setupRoamingNotification() {
        final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
        final boolean upstreamRoamingNotification =
                res.getBoolean(R.bool.config_upstream_roaming_notification);

        if (!upstreamRoamingNotification) return NO_NOTIFY;

        final String title = res.getString(R.string.upstream_roaming_notification_title);
        final String message = res.getString(R.string.upstream_roaming_notification_message);
        if (isEmpty(title) || isEmpty(message)) return NO_NOTIFY;

        final PendingIntent pi = PendingIntent.getActivity(
                mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
                0 /* requestCode */,
                new Intent(Settings.ACTION_TETHER_SETTINGS),
                Intent.FLAG_ACTIVITY_NEW_TASK,
                null /* options */);

        showNotification(R.drawable.stat_sys_tether_general, title, message,
                ROAMING_NOTIFICATION_ID, pi, new Action[0]);
        return NOTIFY_DONE;
    }

    private boolean setupNoUpstreamNotification() {
        final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
        final int delayToShowUpstreamNotification =
Loading