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

Commit 8cad0197 authored by SongFerngWang's avatar SongFerngWang
Browse files

Slice of Provider Model: add slice structure

Implement a slice which can show/update carrier networks.
Before and after: go/b173971144screenshot

Bug: 173971144
Test: atest NetworkProviderWorkerTest  (PASS)
atest ProviderModelSliceTest  (PASS)

Change-Id: I3f0dab364c88723ef3185a2ff040b1fbd1b099f4
parent a97b4faa
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -103,8 +103,10 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment
        implements Indexable, WifiPickerTracker.WifiPickerTrackerCallback,
        WifiDialog2.WifiDialog2Listener, DialogInterface.OnDismissListener {

    private static final String TAG = "NetworkProviderSettings";
    public static final String ACTION_NETWORK_PROVIDER_SETTINGS =
            "android.settings.NETWORK_PROVIDER_SETTINGS";

    private static final String TAG = "NetworkProviderSettings";
    // IDs of context menu
    static final int MENU_ID_CONNECT = Menu.FIRST + 1;
    @VisibleForTesting
+204 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.settings.network;


import static android.app.slice.Slice.EXTRA_TOGGLE_STATE;

import static com.android.settings.slices.CustomSliceRegistry.PROVIDER_MODEL_SLICE_URI;

import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.telephony.SubscriptionManager;
import android.util.Log;

import androidx.annotation.VisibleForTesting;
import androidx.slice.Slice;
import androidx.slice.builders.ListBuilder;

import com.android.settings.R;
import com.android.settings.SubSettings;
import com.android.settings.network.telephony.MobileNetworkUtils;
import com.android.settings.network.telephony.NetworkProviderWorker;
import com.android.settings.slices.CustomSliceable;
import com.android.settings.slices.SliceBackgroundWorker;
import com.android.settings.slices.SliceBuilderUtils;
import com.android.settings.wifi.slice.WifiSlice;
import com.android.settings.wifi.slice.WifiSliceItem;
import com.android.wifitrackerlib.WifiEntry;

import java.util.List;
import java.util.stream.Collectors;

/**
 * {@link CustomSliceable} for Wi-Fi and mobile data connection, used by generic clients.
 */
// ToDo If the provider model become default design in the future, the code needs to refactor
// the whole structure and use new "data object", and then split provider model out of old design.
public class ProviderModelSlice extends WifiSlice {

    private static final String TAG = "ProviderModelSlice";
    private final ProviderModelSliceHelper mHelper;

    public ProviderModelSlice(Context context) {
        super(context);
        mHelper = getHelper();
    }

    @Override
    public Uri getUri() {
        return PROVIDER_MODEL_SLICE_URI;
    }

    private static void log(String s) {
        Log.d(TAG, s);
    }

    protected boolean isApRowCollapsed() {
        return false;
    }

    @Override
    public Slice getSlice() {
        // The provider model slice step:
        // First section:  Add a Wi-Fi item which state is connected.
        // Second section:  Add a carrier item.
        // Third section:  Add the Wi-Fi items which are not connected.
        // Fourth section:  If device has connection problem, this row show the message for user.

        if (mHelper.isAirplaneModeEnabled()) {
            log("Airplane mode is enabled.");
            // ToDo Next CL will add the Airplane mode Message.
            return mHelper.createListBuilder(getUri()).build();
        }

        int maxListSize = 0;
        List<WifiSliceItem> wifiList = null;
        final NetworkProviderWorker worker = getWorker();
        if (worker != null) {
            // get Wi-Fi list.
            wifiList = worker.getResults();
            maxListSize = worker.getApRowCount();
        } else {
            log("network provider worker is null.");
        }

        final boolean hasCarrier = mHelper.hasCarrier();
        log("hasCarrier: " + hasCarrier);


        final ListBuilder listBuilder = mHelper.createListBuilder(getUri());

        // First section:  Add a Wi-Fi item which state is connected.
        final WifiSliceItem connectedWifiItem = mHelper.getConnectedWifiItem(wifiList);
        if (connectedWifiItem != null) {
            log("get Wi-Fi item witch is connected");
            listBuilder.addRow(getWifiSliceItemRow(connectedWifiItem));
            maxListSize--;
        }

        // Second section:  Add a carrier item.
        if (hasCarrier) {
            listBuilder.addRow(mHelper.createCarrierRow());
            maxListSize--;
        }

        // Third section:  Add the Wi-Fi items which are not connected.
        if (wifiList != null) {
            log("get Wi-Fi items which are not connected");
            final List<WifiSliceItem> disconnectedWifiList = wifiList.stream()
                    .filter(wifiSliceItem -> wifiSliceItem.getConnectedState()
                            != WifiEntry.CONNECTED_STATE_CONNECTED)
                    .limit(maxListSize)
                    .collect(Collectors.toList());
            for (WifiSliceItem item : disconnectedWifiList) {
                listBuilder.addRow(getWifiSliceItemRow(item));
            }
        }

        // Fourth section:  If device has connection problem, this row show the message for user.
        // 1) show non_carrier_network_unavailable:
        //    - while no wifi item
        // 2) show all_network_unavailable:
        //    - while no wifi item + no carrier
        //    - while no wifi item + no data capability
        if (worker == null || wifiList == null) {
            log("wifiList is null");
            int resId = R.string.non_carrier_network_unavailable;
            if (!hasCarrier || mHelper.isNoCarrierData()) {
                log("No carrier item or no carrier data.");
                resId = R.string.all_network_unavailable;
            }

            if (!hasCarrier) {
                // If there is no item in ProviderModelItem, slice needs a header.
                listBuilder.setHeader(mHelper.createHeader());
            }
            listBuilder.addGridRow(mHelper.createMessageGridRow(resId));
        }

        return listBuilder.build();
    }

    /**
     * Update the current carrier's mobile data status.
     */
    @Override
    public void onNotifyChange(Intent intent) {
        final SubscriptionManager subscriptionManager = mHelper.getSubscriptionManager();
        if (subscriptionManager == null) {
            return;
        }
        final boolean newState = intent.getBooleanExtra(EXTRA_TOGGLE_STATE,
                mHelper.isMobileDataEnabled());
        final int defaultSubId = subscriptionManager.getDefaultDataSubscriptionId();
        log("defaultSubId:" + defaultSubId);
        if (!SubscriptionManager.isUsableSubscriptionId(defaultSubId)) {
            return; // No subscription - do nothing.
        }

        MobileNetworkUtils.setMobileDataEnabled(mContext, defaultSubId, newState,
                false /* disableOtherSubscriptions */);
    }

    @Override
    public Intent getIntent() {
        final String screenTitle = mContext.getText(R.string.provider_internet_settings).toString();
        return SliceBuilderUtils.buildSearchResultPageIntent(mContext,
                NetworkProviderSettings.class.getName(), "" /* key */, screenTitle,
                SettingsEnums.SLICE)
                .setClassName(mContext.getPackageName(), SubSettings.class.getName())
                .setData(getUri());
    }

    @Override
    public Class getBackgroundWorkerClass() {
        return NetworkProviderWorker.class;
    }

    @VisibleForTesting
    ProviderModelSliceHelper getHelper() {
        return new ProviderModelSliceHelper(mContext, this);
    }

    @VisibleForTesting
    NetworkProviderWorker getWorker() {
        return SliceBackgroundWorker.getInstance(getUri());
    }
}
+234 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.settings.network.telephony;

import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
import android.util.Log;

import androidx.annotation.VisibleForTesting;

import com.android.settings.network.MobileDataContentObserver;
import com.android.settings.network.MobileDataEnabledListener;
import com.android.settings.network.SubscriptionsChangeListener;
import com.android.settings.wifi.slice.WifiScanWorker;

import java.util.Collections;
import java.util.concurrent.Executor;


/**
 * BackgroundWorker for Provider Model slice.
 */
public class NetworkProviderWorker extends WifiScanWorker implements
        SignalStrengthListener.Callback, MobileDataEnabledListener.Client,
        DataConnectivityListener.Client,
        SubscriptionsChangeListener.SubscriptionsChangeListenerClient {
    private static final String TAG = "NetworkProviderWorker";
    private static final int PROVIDER_MODEL_DEFAULT_EXPANDED_ROW_COUNT = 4;
    private DataContentObserver mMobileDataObserver;
    private SignalStrengthListener mSignalStrengthListener;
    private SubscriptionsChangeListener mSubscriptionsListener;
    private MobileDataEnabledListener mDataEnabledListener;
    private DataConnectivityListener mConnectivityListener;

    private final Context mContext;
    @VisibleForTesting
    final PhoneStateListener mPhoneStateListener;
    private final SubscriptionManager mSubscriptionManager;
    private final TelephonyManager mTelephonyManager;

    public NetworkProviderWorker(Context context, Uri uri) {
        super(context, uri);
        // Mobile data worker
        final Handler handler = new Handler(Looper.getMainLooper());
        mMobileDataObserver = new DataContentObserver(handler, this);

        mContext = context;
        mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class);
        mTelephonyManager = mContext.getSystemService(TelephonyManager.class);

        mPhoneStateListener = new NetworkProviderPhoneStateListener(handler::post);
        mSubscriptionsListener = new SubscriptionsChangeListener(context, this);
        mDataEnabledListener = new MobileDataEnabledListener(context, this);
        mConnectivityListener = new DataConnectivityListener(context, this);
        mSignalStrengthListener = new SignalStrengthListener(context, this);
    }

    @Override
    protected void onSlicePinned() {
        mMobileDataObserver.register(mContext,
                getDefaultSubscriptionId(mSubscriptionManager));

        mSubscriptionsListener.start();
        mDataEnabledListener.start(SubscriptionManager.getDefaultDataSubscriptionId());
        mConnectivityListener.start();
        mSignalStrengthListener.resume();
        mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE
                | PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE
                | PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED);

        super.onSlicePinned();
    }

    @Override
    protected void onSliceUnpinned() {
        mMobileDataObserver.unregister(mContext);
        mSubscriptionsListener.stop();
        mDataEnabledListener.stop();
        mConnectivityListener.stop();
        mSignalStrengthListener.pause();
        mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
        super.onSliceUnpinned();
    }

    @Override
    public void close() {
        mMobileDataObserver = null;
        super.close();
    }

    @Override
    public int getApRowCount() {
        return PROVIDER_MODEL_DEFAULT_EXPANDED_ROW_COUNT;
    }

    /**
     * To update the Slice.
     */
    public void updateSlice() {
        notifySliceChange();
    }

    @Override
    public void onSubscriptionsChanged() {
        int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
        Log.d(TAG, "onSubscriptionsChanged: defaultDataSubId:" + defaultDataSubId);

        mSignalStrengthListener.updateSubscriptionIds(
                SubscriptionManager.isUsableSubscriptionId(defaultDataSubId)
                        ? Collections.singleton(defaultDataSubId) : Collections.emptySet());
        if (defaultDataSubId != mDataEnabledListener.getSubId()) {
            mDataEnabledListener.stop();
            mDataEnabledListener.start(defaultDataSubId);
        }
        updateSlice();
    }

    @Override
    public void onSignalStrengthChanged() {
        Log.d(TAG, "onSignalStrengthChanged");
        updateSlice();
    }

    @Override
    public void onAirplaneModeChanged(boolean airplaneModeEnabled) {
        Log.d(TAG, "onAirplaneModeChanged");
        updateSlice();
    }

    @Override
    public void onMobileDataEnabledChange() {
        Log.d(TAG, "onMobileDataEnabledChange");
        updateSlice();
    }

    @Override
    public void onDataConnectivityChange() {
        Log.d(TAG, "onDataConnectivityChange");
        updateSlice();
    }

    /**
     * Listen to update of mobile data change.
     */
    public class DataContentObserver extends ContentObserver {
        private final NetworkProviderWorker mNetworkProviderWorker;

        public DataContentObserver(Handler handler, NetworkProviderWorker backgroundWorker) {
            super(handler);
            mNetworkProviderWorker = backgroundWorker;
        }

        @Override
        public void onChange(boolean selfChange) {
            mNetworkProviderWorker.updateSlice();
        }

        /**
         * To register the observer for mobile data changed.
         * @param context the Context object.
         * @param subId the default data subscription id.
         */
        public void register(Context context, int subId) {
            final Uri uri = MobileDataContentObserver.getObservableUri(context, subId);
            context.getContentResolver().registerContentObserver(uri, false, this);
        }

        /**
         * To unregister the observer for mobile data changed.
         * @param context the Context object.
         */
        public void unregister(Context context) {
            context.getContentResolver().unregisterContentObserver(this);
        }
    }

    class NetworkProviderPhoneStateListener extends PhoneStateListener {
        NetworkProviderPhoneStateListener(Executor executor) {
            super(executor);
        }

        @Override
        public void onServiceStateChanged(ServiceState state) {
            Log.d(TAG, "onServiceStateChanged voiceState=" + state.getState()
                    + " dataState=" + state.getDataRegistrationState());
            updateSlice();
        }

        @Override
        public void onActiveDataSubscriptionIdChanged(int subId) {
            Log.d(TAG, "onActiveDataSubscriptionIdChanged: subId=" + subId);
            updateSlice();
        }

        @Override
        public void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo) {
            Log.d(TAG, "onDisplayInfoChanged: telephonyDisplayInfo=" + telephonyDisplayInfo);
            updateSlice();
        }
    }

    protected static int getDefaultSubscriptionId(SubscriptionManager subscriptionManager) {
        final SubscriptionInfo defaultSubscription = subscriptionManager.getActiveSubscriptionInfo(
                subscriptionManager.getDefaultDataSubscriptionId());

        if (defaultSubscription == null) {
            return SubscriptionManager.INVALID_SUBSCRIPTION_ID; // No default subscription
        }
        return defaultSubscription.getSubscriptionId();
    }
}
+9 −4
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.settings.panel;

import static com.android.settings.network.NetworkProviderSettings.ACTION_NETWORK_PROVIDER_SETTINGS;

import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
@@ -51,17 +53,19 @@ public class InternetConnectivityPanel implements PanelContent {

    @Override
    public CharSequence getTitle() {
        return mContext.getText(R.string.internet_connectivity_panel_title);
        return mContext.getText(Utils.isProviderModelEnabled(mContext)
                ? R.string.provider_internet_settings : R.string.internet_connectivity_panel_title);
    }

    @Override
    public List<Uri> getSlices() {
        final List<Uri> uris = new ArrayList<>();
        uris.add(CustomSliceRegistry.WIFI_SLICE_URI);
        uris.add(CustomSliceRegistry.MOBILE_DATA_SLICE_URI);
        if (Utils.isProviderModelEnabled(mContext)) {
            uris.add(CustomSliceRegistry.PROVIDER_MODEL_SLICE_URI);
            uris.add(CustomSliceRegistry.AIRPLANE_SAFE_NETWORKS_SLICE_URI);
        } else {
            uris.add(CustomSliceRegistry.WIFI_SLICE_URI);
            uris.add(CustomSliceRegistry.MOBILE_DATA_SLICE_URI);
            uris.add(AirplaneModePreferenceController.SLICE_URI);
        }
        return uris;
@@ -69,7 +73,8 @@ public class InternetConnectivityPanel implements PanelContent {

    @Override
    public Intent getSeeMoreIntent() {
        return new Intent(Settings.ACTION_WIRELESS_SETTINGS)
        return new Intent(Utils.isProviderModelEnabled(mContext)
                ? ACTION_NETWORK_PROVIDER_SETTINGS : Settings.ACTION_WIRELESS_SETTINGS)
                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    }

+14 −0
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import com.android.settings.location.LocationSlice;
import com.android.settings.media.MediaOutputIndicatorSlice;
import com.android.settings.media.RemoteMediaSlice;
import com.android.settings.network.AirplaneSafeNetworksSlice;
import com.android.settings.network.ProviderModelSlice;
import com.android.settings.network.telephony.MobileDataSlice;
import com.android.settings.notification.zen.ZenModeButtonPreferenceController;
import com.android.settings.wifi.calling.WifiCallingSliceHelper;
@@ -167,6 +168,17 @@ public class CustomSliceRegistry {
            .appendEncodedPath(SettingsSlicesContract.PATH_SETTING_ACTION)
            .appendPath("mobile_data")
            .build();

    /**
     * Full {@link Uri} for the Provider Model Slice.
     */
    public static final Uri PROVIDER_MODEL_SLICE_URI = new Uri.Builder()
            .scheme(ContentResolver.SCHEME_CONTENT)
            .authority(SettingsSliceProvider.SLICE_AUTHORITY)
            .appendEncodedPath(SettingsSlicesContract.PATH_SETTING_ACTION)
            .appendPath("provider_model")
            .build();

    /**
     * Full {@link Uri} for the Alarm volume Slice.
     */
@@ -176,6 +188,7 @@ public class CustomSliceRegistry {
            .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
            .appendPath("alarm_volume")
            .build();

    /**
     * Full {@link Uri} for the Call Volume Slice.
     */
@@ -319,6 +332,7 @@ public class CustomSliceRegistry {
        sUriToSlice.put(LOW_STORAGE_SLICE_URI, LowStorageSlice.class);
        sUriToSlice.put(MEDIA_OUTPUT_INDICATOR_SLICE_URI, MediaOutputIndicatorSlice.class);
        sUriToSlice.put(MOBILE_DATA_SLICE_URI, MobileDataSlice.class);
        sUriToSlice.put(PROVIDER_MODEL_SLICE_URI, ProviderModelSlice.class);
        sUriToSlice.put(WIFI_SLICE_URI, WifiSlice.class);
        sUriToSlice.put(DARK_THEME_SLICE_URI, DarkThemeSlice.class);
        sUriToSlice.put(REMOTE_MEDIA_SLICE_URI, RemoteMediaSlice.class);
Loading