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

Commit 05d36569 authored by Hugo Benichi's avatar Hugo Benichi Committed by Android (Google) Code Review
Browse files

Merge changes Iadf7f15d,I74702938,Ib8a725cd into nyc-mr2-dev

* changes:
  DO NOT MERGE Network notifications: revamp keying scheme
  DO NOT MERGE Define Network notification proto constants.
  DO NOT MERGE Unit tests for NetworkNotificationManager
parents 64bc2580 84daaea8
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -2219,6 +2219,26 @@ message MetricsEvent {
    // OS: N-MR2
    ACTION_LOCK_BECAUSE_SIM_REMOVED = 497;

    // ACTION: A captive portal was detected during network validation
    // CATEGORY: NOTIFICATION
    // OS: N-MR2
    NOTIFICATION_NETWORK_SIGN_IN = 740;

    // ACTION: An unvalidated network without Internet was selected by the user
    // CATEGORY: NOTIFICATION
    // OS: N-MR2
    NOTIFICATION_NETWORK_NO_INTERNET = 741;

    // ACTION: A validated network failed revalidation and lost Internet access
    // CATEGORY: NOTIFICATION
    // OS: N-MR2
    NOTIFICATION_NETWORK_LOST_INTERNET = 742;

    // ACTION: The system default network switched to a different network
    // CATEGORY: NOTIFICATION
    // OS: N-MR2
    NOTIFICATION_NETWORK_SWITCH = 743;

    // ---- End N-MR2 Constants, all N-MR2 constants go above this line ----

    // Add new aosp constants above this line.
+62 −15
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ package com.android.server.connectivity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.widget.Toast;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
@@ -27,17 +26,40 @@ import android.net.NetworkCapabilities;
import android.os.UserHandle;
import android.telephony.TelephonyManager;
import android.util.Slog;

import android.util.SparseArray;
import android.util.SparseIntArray;
import android.widget.Toast;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsProto.MetricsEvent;

import static android.net.NetworkCapabilities.*;

import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;

public class NetworkNotificationManager {

    public static enum NotificationType { SIGN_IN, NO_INTERNET, LOST_INTERNET, NETWORK_SWITCH };
    public static enum NotificationType {
        LOST_INTERNET(MetricsEvent.NOTIFICATION_NETWORK_LOST_INTERNET),
        NETWORK_SWITCH(MetricsEvent.NOTIFICATION_NETWORK_SWITCH),
        NO_INTERNET(MetricsEvent.NOTIFICATION_NETWORK_NO_INTERNET),
        SIGN_IN(MetricsEvent.NOTIFICATION_NETWORK_SIGN_IN);

        public final int eventId;

        NotificationType(int eventId) {
            this.eventId = eventId;
            Holder.sIdToTypeMap.put(eventId, this);
        }

        private static class Holder {
            private static SparseArray<NotificationType> sIdToTypeMap = new SparseArray<>();
        }

    private static final String NOTIFICATION_ID = "Connectivity.Notification";
        public static NotificationType getFromId(int id) {
            return Holder.sIdToTypeMap.get(id);
        }
    };

    private static final String TAG = NetworkNotificationManager.class.getSimpleName();
    private static final boolean DBG = true;
@@ -46,11 +68,14 @@ public class NetworkNotificationManager {
    private final Context mContext;
    private final TelephonyManager mTelephonyManager;
    private final NotificationManager mNotificationManager;
    // Tracks the types of notifications managed by this instance, from creation to cancellation.
    private final SparseIntArray mNotificationTypeMap;

    public NetworkNotificationManager(Context c, TelephonyManager t, NotificationManager n) {
        mContext = c;
        mTelephonyManager = t;
        mNotificationManager = n;
        mNotificationTypeMap = new SparseIntArray();
    }

    // TODO: deal more gracefully with multi-transport networks.
@@ -100,8 +125,10 @@ public class NetworkNotificationManager {
     */
    public void showNotification(int id, NotificationType notifyType, NetworkAgentInfo nai,
            NetworkAgentInfo switchToNai, PendingIntent intent, boolean highPriority) {
        int transportType;
        String extraInfo;
        final String tag = tagFor(id);
        final int eventId = notifyType.eventId;
        final int transportType;
        final String extraInfo;
        if (nai != null) {
            transportType = getFirstTransportType(nai);
            extraInfo = nai.networkInfo.getExtraInfo();
@@ -114,9 +141,9 @@ public class NetworkNotificationManager {
        }

        if (DBG) {
            Slog.d(TAG, "showNotification id=" + id + " " + notifyType
                    + " transportType=" + getTransportName(transportType)
                    + " extraInfo=" + extraInfo + " highPriority=" + highPriority);
            Slog.d(TAG, String.format(
                    "showNotification tag=%s event=%s transport=%s extraInfo=%d highPrioriy=%s",
                    tag, nameOf(eventId), getTransportName(transportType), extraInfo, highPriority));
        }

        Resources r = Resources.getSystem();
@@ -184,22 +211,31 @@ public class NetworkNotificationManager {

        Notification notification = builder.build();

        mNotificationTypeMap.put(id, eventId);
        try {
            mNotificationManager.notifyAsUser(NOTIFICATION_ID, id, notification, UserHandle.ALL);
            mNotificationManager.notifyAsUser(tag, eventId, notification, UserHandle.ALL);
        } catch (NullPointerException npe) {
            Slog.d(TAG, "setNotificationVisible: visible notificationManager error", npe);
        }
    }

    public void clearNotification(int id) {
        final String tag = tagFor(id);
        if (mNotificationTypeMap.indexOfKey(id) < 0) {
            Slog.e(TAG, "cannot clear unknown notification with tag=" + tag);
            return;
        }
        final int eventId = mNotificationTypeMap.get(id);
        if (DBG) {
            Slog.d(TAG, "clearNotification id=" + id);
            Slog.d(TAG, String.format("clearing notification tag=%s event=", tag, nameOf(eventId)));
        }
        try {
            mNotificationManager.cancelAsUser(NOTIFICATION_ID, id, UserHandle.ALL);
            mNotificationManager.cancelAsUser(tag, eventId, UserHandle.ALL);
        } catch (NullPointerException npe) {
            Slog.d(TAG, "setNotificationVisible: cancel notificationManager error", npe);
            Slog.d(TAG, String.format(
                    "failed to clear notification tag=%s event=", tag, nameOf(eventId)), npe);
        }
        mNotificationTypeMap.delete(id);
    }

    /**
@@ -222,4 +258,15 @@ public class NetworkNotificationManager {
                R.string.network_switch_metered_toast, fromTransport, toTransport);
        Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
    }

    @VisibleForTesting
    static String tagFor(int id) {
        return String.format("ConnectivityNotification:%d", id);
    }

    @VisibleForTesting
    static String nameOf(int eventId) {
        NotificationType t = NotificationType.getFromId(eventId);
        return (t != null) ? t.name() : "UNKNOWN";
    }
}
+141 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 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.server.connectivity;

import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.telephony.TelephonyManager;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import junit.framework.TestCase;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;

import static com.android.server.connectivity.NetworkNotificationManager.NotificationType.*;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class NetworkNotificationManagerTest extends TestCase {

    static final NetworkCapabilities CELL_CAPABILITIES = new NetworkCapabilities();
    static final NetworkCapabilities WIFI_CAPABILITIES = new NetworkCapabilities();
    static {
        CELL_CAPABILITIES.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
        CELL_CAPABILITIES.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);

        WIFI_CAPABILITIES.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
        WIFI_CAPABILITIES.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
    }

    @Mock Context mCtx;
    @Mock Resources mResources;
    @Mock PackageManager mPm;
    @Mock TelephonyManager mTelephonyManager;
    @Mock NotificationManager mNotificationManager;
    @Mock NetworkAgentInfo mWifiNai;
    @Mock NetworkAgentInfo mCellNai;
    @Mock NetworkInfo mNetworkInfo;
    ArgumentCaptor<Notification> mCaptor;

    NetworkNotificationManager mManager;

    public void setUp() {
        MockitoAnnotations.initMocks(this);
        mCaptor = ArgumentCaptor.forClass(Notification.class);
        mWifiNai.networkCapabilities = WIFI_CAPABILITIES;
        mWifiNai.networkInfo = mNetworkInfo;
        mCellNai.networkCapabilities = CELL_CAPABILITIES;
        mCellNai.networkInfo = mNetworkInfo;
        when(mCtx.getResources()).thenReturn(mResources);
        when(mCtx.getPackageManager()).thenReturn(mPm);
        when(mCtx.getApplicationInfo()).thenReturn(new ApplicationInfo());
        when(mResources.getColor(anyInt(), any())).thenReturn(0xFF607D8B);

        mManager = new NetworkNotificationManager(mCtx, mTelephonyManager, mNotificationManager);
    }

    @SmallTest
    public void testNotificationsShownAndCleared() {
        final int NETWORK_ID_BASE = 100;
        List<NotificationType> types = Arrays.asList(NotificationType.values());
        List<Integer> ids = new ArrayList<>(types.size());
        for (int i = 0; i < ids.size(); i++) {
            ids.add(NETWORK_ID_BASE + i);
        }
        Collections.shuffle(ids);
        Collections.shuffle(types);

        for (int i = 0; i < ids.size(); i++) {
            mManager.showNotification(ids.get(i), types.get(i), mWifiNai, mCellNai, null, false);
        }

        Collections.shuffle(ids);
        for (int i = 0; i < ids.size(); i++) {
            mManager.clearNotification(ids.get(i));
        }

        for (int i = 0; i < ids.size(); i++) {
            final int id = ids.get(i);
            final int eventId = types.get(i).eventId;
            final String tag = NetworkNotificationManager.tagFor(id);
            verify(mNotificationManager, times(1)).notifyAsUser(eq(tag), eq(eventId), any(), any());
            verify(mNotificationManager, times(1)).cancelAsUser(eq(tag), eq(eventId), any());
        }
    }

    @SmallTest
    public void testNoInternetNotificationsNotShownForCellular() {
        mManager.showNotification(100, NO_INTERNET, mCellNai, mWifiNai, null, false);
        mManager.showNotification(101, LOST_INTERNET, mCellNai, mWifiNai, null, false);

        verify(mNotificationManager, never()).notifyAsUser(any(), anyInt(), any(), any());

        mManager.showNotification(102, NO_INTERNET, mWifiNai, mCellNai, null, false);

        final int eventId = NO_INTERNET.eventId;
        final String tag = NetworkNotificationManager.tagFor(102);
        verify(mNotificationManager, times(1)).notifyAsUser(eq(tag), eq(eventId), any(), any());
    }

    @SmallTest
    public void testNotificationsNotShownIfNoInternetCapability() {
        mWifiNai.networkCapabilities = new NetworkCapabilities();
        mWifiNai.networkCapabilities .addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
        mManager.showNotification(102, NO_INTERNET, mWifiNai, mCellNai, null, false);
        mManager.showNotification(103, LOST_INTERNET, mWifiNai, mCellNai, null, false);
        mManager.showNotification(104, NETWORK_SWITCH, mWifiNai, mCellNai, null, false);

        verify(mNotificationManager, never()).notifyAsUser(any(), anyInt(), any(), any());
    }
}