From 2818dedc284dd7afd3ced6165b9850c689b6b624 Mon Sep 17 00:00:00 2001 From: changbetty Date: Tue, 26 Apr 2022 07:47:23 +0000 Subject: [PATCH 01/13] [LE] Launch the MediaOutputBroadcastDialog when the current device is broadcasting Bug: 229577323 Test: Manual test Change-Id: I0bfc9ecac2a0b6d68d41c4d17d4642f927581ccb --- .../notification/MediaVolumePreferenceController.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/notification/MediaVolumePreferenceController.java b/src/com/android/settings/notification/MediaVolumePreferenceController.java index 978a4b40237..c0d5610c932 100644 --- a/src/com/android/settings/notification/MediaVolumePreferenceController.java +++ b/src/com/android/settings/notification/MediaVolumePreferenceController.java @@ -35,6 +35,7 @@ import com.android.settings.media.MediaOutputIndicatorWorker; import com.android.settings.slices.CustomSliceRegistry; import com.android.settings.slices.SliceBackgroundWorker; import com.android.settingslib.media.MediaDevice; +import com.android.settingslib.media.MediaOutputConstants; public class MediaVolumePreferenceController extends VolumeSeekBarPreferenceController { private static final String TAG = "MediaVolumePreCtrl"; @@ -106,8 +107,10 @@ public class MediaVolumePreferenceController extends VolumeSeekBarPreferenceCont final Intent intent = new Intent(); if (getWorker().isDeviceBroadcasting()) { - // TODO(b/229577323) : Get the intent action for the Media Output Broadcast Dialog - // in SystemUI + intent.setPackage(MediaOutputConstants.SYSTEMUI_PACKAGE_NAME); + intent.setAction(MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG); + intent.putExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME, + getWorker().getActiveLocalMediaController().getPackageName()); } else { // TODO(b/229577518) : Get the intent action of the Bluetooth Broadcast Dialog // for user to choose the action From 263592e3dd2cba5769bdede8cc11244de94f4fd9 Mon Sep 17 00:00:00 2001 From: Arc Wang Date: Thu, 6 Jan 2022 10:58:58 +0800 Subject: [PATCH 02/13] Prevent side channel package installation enumeration From Android 11, apps need the permission QUERY_ALL_PACKAGES to probe existence of arbitrary installed packages. However, an Activity which declares android:scheme="package in intent-filter may be vulnerable and attacker app can use it to probe installed packages. This change add permission QUERY_ALL_PACKAGES to protect vulnerable Activity. Bug: 185477439 Test: Install POC and check if it can probe installed packages by each vulnerable Activity. Change-Id: I521545436102f72f2e0c5053e30fd03bd6bc756f --- AndroidManifest.xml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 4d3b6d0e3f9..1f9c9086c48 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1451,7 +1451,8 @@ + android:noHistory="true" + android:permission="android.permission.QUERY_ALL_PACKAGES"> @@ -1521,6 +1522,7 @@ @@ -1534,7 +1536,8 @@ + android:exported="true" + android:permission="android.permission.QUERY_ALL_PACKAGES"> @@ -1893,7 +1896,8 @@ + android:label="@string/usage_access_title" + android:permission="android.permission.QUERY_ALL_PACKAGES"> From 646e9e4ff8f75578b015f2d9d0633cf8a85b3bc8 Mon Sep 17 00:00:00 2001 From: Yuri Lin Date: Wed, 20 Apr 2022 15:49:05 -0400 Subject: [PATCH 03/13] Move REVIEW_PERMISSIONS_NOTIFICATION_STATE to Global from Secure This is a global setting that is not per-user, so adjusting it accordingly. Bug: 225373531 Test: manual Change-Id: I100cd53a5ffef6ce5f9a01e6add9465a00ceaeb5 --- .../applications/manageapplications/ManageApplications.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java index 3eae8855959..062bfc576b2 100644 --- a/src/com/android/settings/applications/manageapplications/ManageApplications.java +++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java @@ -330,8 +330,8 @@ public class ManageApplications extends InstrumentedFragment if (className.equals(Settings.NotificationReviewPermissionsActivity.class.getName())) { // Special-case for a case where a user is directed to the all apps notification // preferences page via a notification prompt to review permissions settings. - android.provider.Settings.Secure.putInt(getContext().getContentResolver(), - android.provider.Settings.Secure.REVIEW_PERMISSIONS_NOTIFICATION_STATE, + android.provider.Settings.Global.putInt(getContext().getContentResolver(), + android.provider.Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE, 1); // USER_INTERACTED } } else if (className.equals(AppLocaleDetails.class.getName())) { From 049aa73cb1018dcbfea10650dec26e3a0de2d552 Mon Sep 17 00:00:00 2001 From: Calvin Pan Date: Mon, 25 Apr 2022 11:29:39 +0000 Subject: [PATCH 04/13] Add unit test for AppLocalePickerActivity Bug: 223503670 Test: atest Change-Id: I0e92be43d661dd3bff19c3274d1572ef1f4e9c46 --- .../localepicker/AppLocalePickerActivity.java | 8 +- .../AppLocalePickerActivityTest.java | 118 ++++++++++++++++++ 2 files changed, 119 insertions(+), 7 deletions(-) create mode 100644 tests/robotests/src/com/android/settings/localepicker/AppLocalePickerActivityTest.java diff --git a/src/com/android/settings/localepicker/AppLocalePickerActivity.java b/src/com/android/settings/localepicker/AppLocalePickerActivity.java index 4700f3f5383..1ed6f5be3b1 100644 --- a/src/com/android/settings/localepicker/AppLocalePickerActivity.java +++ b/src/com/android/settings/localepicker/AppLocalePickerActivity.java @@ -30,7 +30,6 @@ import android.view.View; import android.widget.FrameLayout; import android.widget.ListView; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.LocalePickerWithRegion; import com.android.internal.app.LocaleStore; import com.android.settings.R; @@ -38,10 +37,6 @@ import com.android.settings.applications.AppInfoBase; import com.android.settings.applications.appinfo.AppLocaleDetails; import com.android.settings.core.SettingsBaseActivity; -/** - * TODO(b/223503670): Add unit test for AppLocalePickerActivity. - * A activity to show the locale picker and information page. - */ public class AppLocalePickerActivity extends SettingsBaseActivity implements LocalePickerWithRegion.LocaleSelectedListener { private static final String TAG = AppLocalePickerActivity.class.getSimpleName(); @@ -128,8 +123,7 @@ public class AppLocalePickerActivity extends SettingsBaseActivity return appLocaleDetailsContainer; } - @VisibleForTesting - void launchLocalePickerPage() { + private void launchLocalePickerPage() { // LocalePickerWithRegion use android.app.ListFragment. Thus, it can not use // getSupportFragmentManager() to add this into container. android.app.FragmentManager fragmentManager = getFragmentManager(); diff --git a/tests/robotests/src/com/android/settings/localepicker/AppLocalePickerActivityTest.java b/tests/robotests/src/com/android/settings/localepicker/AppLocalePickerActivityTest.java new file mode 100644 index 00000000000..4d5e0b98172 --- /dev/null +++ b/tests/robotests/src/com/android/settings/localepicker/AppLocalePickerActivityTest.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2022 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.localepicker; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.Activity; +import android.app.ApplicationPackageManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.InstallSourceInfo; +import android.net.Uri; +import android.os.Process; +import android.os.UserHandle; +import android.telephony.TelephonyManager; + +import androidx.fragment.app.Fragment; + +import com.android.settings.R; +import com.android.settings.applications.AppInfoBase; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.Shadows; +import org.robolectric.android.controller.ActivityController; +import org.robolectric.annotation.Config; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; +import org.robolectric.shadows.ShadowTelephonyManager; + +@RunWith(RobolectricTestRunner.class) +@Config( + shadows = { + AppLocalePickerActivityTest.ShadowApplicationPackageManager.class, + }) +public class AppLocalePickerActivityTest { + private static final String TEST_PACKAGE_NAME = "com.android.settings"; + private static final Uri TEST_PACKAGE_URI = Uri.parse("package:" + TEST_PACKAGE_NAME); + + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @Test + public void launchAppLocalePickerActivity_hasPackageName_success() { + ActivityController controller = + initActivityController(true); + + controller.create(); + + assertThat(controller.get().isFinishing()).isFalse(); + } + + @Test + public void launchAppLocalePickerActivity_intentWithoutPackageName_failed() { + ActivityController controller = + initActivityController(false); + + controller.create(); + + assertThat(controller.get().isFinishing()).isTrue(); + } + + private ActivityController initActivityController( + boolean hasPackageName) { + Intent data = new Intent(); + if (hasPackageName) { + data.setData(TEST_PACKAGE_URI); + } + data.putExtra(AppInfoBase.ARG_PACKAGE_UID, UserHandle.getUserId(Process.myUid())); + ActivityController activityController = + Robolectric.buildActivity(TestAppLocalePickerActivity.class, data); + Activity activity = activityController.get(); + + ShadowTelephonyManager shadowTelephonyManager = Shadows.shadowOf( + activity.getSystemService(TelephonyManager.class)); + shadowTelephonyManager.setSimCountryIso("US"); + shadowTelephonyManager.setNetworkCountryIso("US"); + + return activityController; + } + + private static class TestAppLocalePickerActivity extends AppLocalePickerActivity { + @Override + public Context createContextAsUser(UserHandle user, int flags) { + // return the current context as a work profile + return this; + } + } + + @Implements(ApplicationPackageManager.class) + public static class ShadowApplicationPackageManager extends + org.robolectric.shadows.ShadowApplicationPackageManager { + + @Implementation + protected Object getInstallSourceInfo(String packageName) { + return new InstallSourceInfo("", null, null, ""); + } + } +} From b899bd3690e8b7a0040e041c0f44a700a8d8a133 Mon Sep 17 00:00:00 2001 From: tom hsu Date: Thu, 28 Apr 2022 21:32:33 +0800 Subject: [PATCH 05/13] [Panlingual] Avoid activity restart during rotation Bug: 230680942 Test: local Change-Id: Ib64509fb73fb363ad4dd5c2163659346295f581d --- AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 762d84da36e..15c5272665b 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -866,6 +866,7 @@ From ddedb31f6e94253b35c413e8e0bdca98355cc37e Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Thu, 28 Apr 2022 19:09:02 +0800 Subject: [PATCH 06/13] Fix flicker for Mobile data & Wi-Fi page Both "Mobile data usage" & "Non-carrier data usage". By, 1. Add header in onCreate() instead of onViewCreated() 2. Keep the space for Spinner, and preload initial cycles 3. Keep the space for 3 usage summaries Bug: 191730864 Test: manual Change-Id: I8c309c5f51ce6290383a2d10f75e41d0f207d61a --- res/xml/app_data_usage.xml | 12 +- .../settings/datausage/AppDataUsage.java | 26 +++- .../settings/datausage/CycleAdapter.java | 141 +----------------- .../settings/datausage/SpinnerPreference.java | 14 ++ .../settings/datausage/AppDataUsageTest.java | 20 +-- 5 files changed, 53 insertions(+), 160 deletions(-) diff --git a/res/xml/app_data_usage.xml b/res/xml/app_data_usage.xml index e64a1c5cc63..e94c4ff4ca2 100644 --- a/res/xml/app_data_usage.xml +++ b/res/xml/app_data_usage.xml @@ -21,8 +21,7 @@ android:title="@string/data_usage_app_summary_title"> + android:key="cycle" /> @@ -31,19 +30,22 @@ android:key="total_usage" android:title="@string/total_size_label" android:selectable="false" - android:layout="@layout/horizontal_preference" /> + android:layout="@layout/horizontal_preference" + android:summary="@string/summary_placeholder" /> + android:layout="@layout/horizontal_preference" + android:summary="@string/summary_placeholder" /> + android:layout="@layout/horizontal_preference" + android:summary="@string/summary_placeholder" /> diff --git a/src/com/android/settings/datausage/AppDataUsage.java b/src/com/android/settings/datausage/AppDataUsage.java index 460f3909f74..531a341c95a 100644 --- a/src/com/android/settings/datausage/AppDataUsage.java +++ b/src/com/android/settings/datausage/AppDataUsage.java @@ -144,8 +144,7 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC mForegroundUsage = findPreference(KEY_FOREGROUND_USAGE); mBackgroundUsage = findPreference(KEY_BACKGROUND_USAGE); - mCycle = findPreference(KEY_CYCLE); - mCycleAdapter = new CycleAdapter(mContext, mCycle, mCycleListener); + initCycle(); final UidDetailProvider uidDetailProvider = getUidDetailProvider(); @@ -211,6 +210,8 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC removePreference(KEY_RESTRICT_BACKGROUND); removePreference(KEY_APP_LIST); } + + addEntityHeader(); } @Override @@ -276,6 +277,17 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC return new UidDetailProvider(mContext); } + private void initCycle() { + mCycle = findPreference(KEY_CYCLE); + mCycleAdapter = new CycleAdapter(mContext, mCycle, mCycleListener); + if (mCycles != null) { + // If coming from a page like DataUsageList where already has a selected cycle, display + // that before loading to reduce flicker. + mCycleAdapter.setInitialCycleList(mCycles, mSelectedCycle); + mCycle.setHasCycles(true); + } + } + private void updatePrefs(boolean restrictBackground, boolean unrestrictData) { final EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfMeteredDataRestricted( mContext, mPackageName, UserHandle.getUserId(mAppItem.key)); @@ -308,9 +320,9 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC final long backgroundBytes, foregroundBytes; if (mUsageData == null || position >= mUsageData.size()) { backgroundBytes = foregroundBytes = 0; - mCycle.setVisible(false); + mCycle.setHasCycles(false); } else { - mCycle.setVisible(true); + mCycle.setHasCycles(true); final NetworkCycleDataForUid data = mUsageData.get(position); backgroundBytes = data.getBackgroudUsage(); foregroundBytes = data.getForegroudUsage(); @@ -335,10 +347,8 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC return false; } - @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - + @VisibleForTesting + void addEntityHeader() { String pkg = mPackages.size() != 0 ? mPackages.valueAt(0) : null; int uid = 0; if (pkg != null) { diff --git a/src/com/android/settings/datausage/CycleAdapter.java b/src/com/android/settings/datausage/CycleAdapter.java index 2cabd8de452..b41b6aad91b 100644 --- a/src/com/android/settings/datausage/CycleAdapter.java +++ b/src/com/android/settings/datausage/CycleAdapter.java @@ -13,24 +13,13 @@ */ package com.android.settings.datausage; -import android.annotation.NonNull; -import android.app.usage.NetworkStats; import android.content.Context; -import android.net.NetworkPolicy; -import android.net.NetworkPolicyManager; -import android.text.format.DateUtils; -import android.util.Pair; -import android.util.Range; import android.widget.AdapterView; -import com.android.net.module.util.NetworkStatsUtils; import com.android.settings.Utils; -import com.android.settingslib.net.ChartData; import com.android.settingslib.net.NetworkCycleData; import com.android.settingslib.widget.SettingsSpinnerAdapter; -import java.time.ZonedDateTime; -import java.util.Iterator; import java.util.List; import java.util.Objects; @@ -45,7 +34,6 @@ public class CycleAdapter extends SettingsSpinnerAdapter mSpinner = spinner; mListener = listener; mSpinner.setAdapter(this); - mSpinner.setOnItemSelectedListener(mListener); } /** @@ -65,128 +53,14 @@ public class CycleAdapter extends SettingsSpinnerAdapter return 0; } - protected static long getTotalBytesForTimeRange(List stats, - Range range) { - long bytes = 0L; - for (NetworkStats.Bucket bucket : stats) { - final Range bucketSpan = new Range<>( - bucket.getStartTimeStamp(), bucket.getEndTimeStamp()); - // Only record bytes that overlapped with the given time range. For partially - // overlapped bucket, record rational bytes assuming the traffic is uniform - // distributed within the bucket. - try { - final Range overlapped = range.intersect(bucketSpan); - final long totalOfBucket = bucket.getRxBytes() + bucket.getTxBytes(); - bytes += NetworkStatsUtils.multiplySafeByRational(totalOfBucket, - overlapped.getUpper() - overlapped.getLower(), - bucketSpan.getUpper() - bucketSpan.getLower()); - } catch (IllegalArgumentException e) { - // Range disjoint, ignore. - continue; - } - } - return bytes; - } - - @NonNull - private Range getTimeRangeOf(@NonNull List stats) { - long start = Long.MAX_VALUE; - long end = Long.MIN_VALUE; - for (NetworkStats.Bucket bucket : stats) { - start = Math.min(start, bucket.getStartTimeStamp()); - end = Math.max(end, bucket.getEndTimeStamp()); - } - return new Range(start, end); - } - - /** - * Rebuild list based on {@link NetworkPolicy} and available - * {@link List} data. Always selects the newest item, - * updating the inspection range on chartData. - */ - @Deprecated - public boolean updateCycleList(NetworkPolicy policy, ChartData chartData) { - // stash away currently selected cycle to try restoring below - final CycleAdapter.CycleItem previousItem = (CycleAdapter.CycleItem) - mSpinner.getSelectedItem(); + void setInitialCycleList(List cycles, long selectedCycle) { clear(); - - final Context context = getContext(); - - long historyStart; - long historyEnd; - try { - final Range historyTimeRange = getTimeRangeOf(chartData.network); - historyStart = historyTimeRange.getLower(); - historyEnd = historyTimeRange.getUpper(); - } catch (IllegalArgumentException e) { - // Empty history. - final long now = System.currentTimeMillis(); - historyStart = now; - historyEnd = now + 1; - } - - boolean hasCycles = false; - if (policy != null) { - final Iterator> it = NetworkPolicyManager - .cycleIterator(policy); - while (it.hasNext()) { - final Pair cycle = it.next(); - final long cycleStart = cycle.first.toInstant().toEpochMilli(); - final long cycleEnd = cycle.second.toInstant().toEpochMilli(); - - final boolean includeCycle; - if (chartData != null) { - final long bytesInCycle = getTotalBytesForTimeRange(chartData.network, - new Range<>(cycleStart, cycleEnd)); - includeCycle = bytesInCycle > 0; - } else { - includeCycle = true; - } - - if (includeCycle) { - add(new CycleAdapter.CycleItem(context, cycleStart, cycleEnd)); - hasCycles = true; - } + for (int i = 0; i < cycles.size() - 1; i++) { + add(new CycleAdapter.CycleItem(getContext(), cycles.get(i + 1), cycles.get(i))); + if (cycles.get(i) == selectedCycle) { + mSpinner.setSelection(i); } } - - if (!hasCycles) { - // no policy defined cycles; show entry for each four-week period - long cycleEnd = historyEnd; - while (cycleEnd > historyStart) { - final long cycleStart = cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4); - - final boolean includeCycle; - if (chartData != null) { - final long bytesInCycle = getTotalBytesForTimeRange(chartData.network, - new Range<>(cycleStart, cycleEnd)); - includeCycle = bytesInCycle > 0; - } else { - includeCycle = true; - } - - if (includeCycle) { - add(new CycleAdapter.CycleItem(context, cycleStart, cycleEnd)); - } - cycleEnd = cycleStart; - } - } - - // force pick the current cycle (first item) - if (getCount() > 0) { - final int position = findNearestPosition(previousItem); - mSpinner.setSelection(position); - - // only force-update cycle when changed; skipping preserves any - // user-defined inspection region. - final CycleAdapter.CycleItem selectedItem = getItem(position); - if (!Objects.equals(selectedItem, previousItem)) { - mListener.onItemSelected(null, null, position, 0); - return false; - } - } - return true; } /** @@ -194,6 +68,7 @@ public class CycleAdapter extends SettingsSpinnerAdapter * updating the inspection range on chartData. */ public boolean updateCycleList(List cycleData) { + mSpinner.setOnItemSelectedListener(mListener); // stash away currently selected cycle to try restoring below final CycleAdapter.CycleItem previousItem = (CycleAdapter.CycleItem) mSpinner.getSelectedItem(); @@ -228,10 +103,6 @@ public class CycleAdapter extends SettingsSpinnerAdapter public long start; public long end; - public CycleItem(CharSequence label) { - this.label = label; - } - public CycleItem(Context context, long start, long end) { this.label = Utils.formatDateRange(context, start, end); this.start = start; diff --git a/src/com/android/settings/datausage/SpinnerPreference.java b/src/com/android/settings/datausage/SpinnerPreference.java index 867930baa97..c4b7a4e18da 100644 --- a/src/com/android/settings/datausage/SpinnerPreference.java +++ b/src/com/android/settings/datausage/SpinnerPreference.java @@ -31,6 +31,8 @@ public class SpinnerPreference extends Preference implements CycleAdapter.Spinne private AdapterView.OnItemSelectedListener mListener; private Object mCurrentObject; private int mPosition; + private View mItemView; + private boolean mItemViewVisible = false; public SpinnerPreference(Context context, AttributeSet attrs) { super(context, attrs); @@ -63,12 +65,24 @@ public class SpinnerPreference extends Preference implements CycleAdapter.Spinne @Override public void onBindViewHolder(PreferenceViewHolder holder) { super.onBindViewHolder(holder); + mItemView = holder.itemView; + mItemView.setVisibility(mItemViewVisible ? View.VISIBLE : View.INVISIBLE); Spinner spinner = (Spinner) holder.findViewById(R.id.cycles_spinner); spinner.setAdapter(mAdapter); spinner.setSelection(mPosition); spinner.setOnItemSelectedListener(mOnSelectedListener); } + void setHasCycles(boolean hasData) { + setVisible(hasData); + if (hasData) { + mItemViewVisible = true; + if (mItemView != null) { + mItemView.setVisibility(View.VISIBLE); + } + } + } + @Override protected void performClick(View view) { view.findViewById(R.id.cycles_spinner).performClick(); diff --git a/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java b/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java index 09c57340d60..f043ec737e3 100644 --- a/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java +++ b/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java @@ -43,7 +43,6 @@ import android.os.Process; import android.telephony.SubscriptionManager; import android.text.format.DateUtils; import android.util.ArraySet; -import android.view.View; import androidx.fragment.app.FragmentActivity; import androidx.preference.Preference; @@ -96,6 +95,10 @@ public class AppDataUsageTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); + + ShadowEntityHeaderController.setUseMock(mHeaderController); + when(mHeaderController.setRecyclerView(any(), any())).thenReturn(mHeaderController); + when(mHeaderController.setUid(anyInt())).thenReturn(mHeaderController); } @After @@ -163,10 +166,6 @@ public class AppDataUsageTest { @Test public void bindAppHeader_allWorkApps_shouldNotShowAppInfoLink() { - ShadowEntityHeaderController.setUseMock(mHeaderController); - when(mHeaderController.setRecyclerView(any(), any())).thenReturn(mHeaderController); - when(mHeaderController.setUid(anyInt())).thenReturn(mHeaderController); - mFragment = spy(new AppDataUsage()); when(mFragment.getPreferenceManager()) @@ -174,7 +173,7 @@ public class AppDataUsageTest { doReturn(mock(PreferenceScreen.class)).when(mFragment).getPreferenceScreen(); ReflectionHelpers.setField(mFragment, "mAppItem", mock(AppItem.class)); - mFragment.onViewCreated(new View(RuntimeEnvironment.application), new Bundle()); + mFragment.addEntityHeader(); verify(mHeaderController).setHasAppInfoLink(false); } @@ -196,16 +195,13 @@ public class AppDataUsageTest { when(mPackageManager.getPackageUidAsUser(anyString(), anyInt())) .thenReturn(fakeUserId); - ShadowEntityHeaderController.setUseMock(mHeaderController); - when(mHeaderController.setRecyclerView(any(), any())).thenReturn(mHeaderController); - when(mHeaderController.setUid(fakeUserId)).thenReturn(mHeaderController); when(mHeaderController.setHasAppInfoLink(anyBoolean())).thenReturn(mHeaderController); when(mFragment.getPreferenceManager()) .thenReturn(mock(PreferenceManager.class, RETURNS_DEEP_STUBS)); doReturn(mock(PreferenceScreen.class)).when(mFragment).getPreferenceScreen(); - mFragment.onViewCreated(new View(RuntimeEnvironment.application), new Bundle()); + mFragment.addEntityHeader(); verify(mHeaderController).setHasAppInfoLink(true); verify(mHeaderController).setUid(fakeUserId); @@ -268,7 +264,7 @@ public class AppDataUsageTest { mFragment.bindData(0 /* position */); - verify(cycle).setVisible(false); + verify(cycle).setHasCycles(false); } @Test @@ -293,7 +289,7 @@ public class AppDataUsageTest { mFragment.bindData(0 /* position */); - verify(cycle).setVisible(true); + verify(cycle).setHasCycles(true); verify(totalPref).setSummary( DataUsageUtils.formatDataUsage(context, backgroundBytes + foregroundBytes)); verify(backgroundPref).setSummary(DataUsageUtils.formatDataUsage(context, backgroundBytes)); From c15fb9ed814e02e51292f823623e0b81968b6707 Mon Sep 17 00:00:00 2001 From: Jan Tomljanovic Date: Thu, 28 Apr 2022 17:09:05 +0100 Subject: [PATCH 07/13] Update the SafetyCenter icon Test: manual Bug: 230731934 Change-Id: I11a82348b77b5e162e9dfaf5bfa91642db709f6f --- res/drawable/ic_settings_safety_center.xml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/res/drawable/ic_settings_safety_center.xml b/res/drawable/ic_settings_safety_center.xml index f43359fefd4..e817b7194bb 100644 --- a/res/drawable/ic_settings_safety_center.xml +++ b/res/drawable/ic_settings_safety_center.xml @@ -13,17 +13,19 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - + android:viewportHeight="24" + android:tint="?android:attr/textColorPrimary"> + + android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" /> + android:fillColor="#000000" + android:pathData="M12,4.14l6,2.25v4.71c0,2.12-0.62,4-1.88,5.74c-1.13,1.55-2.48,2.56-4.12,3.09c-1.64-0.52-2.99-1.54-4.12-3.09 C6.62,15.1,6,13.22,6,11.1V6.39L12,4.14 M12,2L4,5v6.1c0,2.53,0.75,4.84,2.26,6.91C7.77,20.09,9.68,21.42,12,22 c2.32-0.58,4.23-1.91,5.74-3.99C19.25,15.94,20,13.63,20,11.1V5L12,2L12,2z" /> + \ No newline at end of file From 4e34e65228a9350dbb561e77761c7bad0a23a9fc Mon Sep 17 00:00:00 2001 From: Weng Su Date: Fri, 29 Apr 2022 02:22:05 +0800 Subject: [PATCH 08/13] Show restricted message in Wi-Fi hotspot summary - Refine WifiTetherPreferenceController - See the result screenshot in b/203168953#comment8 Bug: 203168953 Test: manual test make RunSettingsRoboTests \ ROBOTEST_FILTER=WifiTetherPreferenceControllerTest Change-Id: If094178eb0cd9ccf20ff3899dc4b087b45c66f6b --- .../WifiTetherPreferenceController.java | 48 +++++++++++------- .../WifiTetherPreferenceControllerTest.java | 49 ++++++++----------- 2 files changed, 51 insertions(+), 46 deletions(-) diff --git a/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java index 94bd78a9c09..73ff31d4e47 100644 --- a/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java +++ b/src/com/android/settings/wifi/tether/WifiTetherPreferenceController.java @@ -46,12 +46,9 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController private static final String WIFI_TETHER_SETTINGS = "wifi_tether"; - private final TetheringManager mTetheringManager; - private final String[] mWifiRegexs; - private final WifiManager mWifiManager; - private final Lifecycle mLifecycle; - @VisibleForTesting - boolean mIsWifiTetheringAllow; + private boolean mIsWifiTetherable; + private WifiManager mWifiManager; + private boolean mIsWifiTetheringAllow; private int mSoftApState; @VisibleForTesting Preference mPreference; @@ -59,18 +56,32 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController WifiTetherSoftApManager mWifiTetherSoftApManager; public WifiTetherPreferenceController(Context context, Lifecycle lifecycle) { - this(context, lifecycle, true /* initSoftApManager */); + this(context, lifecycle, + context.getSystemService(WifiManager.class), + context.getSystemService(TetheringManager.class), + true /* initSoftApManager */, + WifiEnterpriseRestrictionUtils.isWifiTetheringAllowed(context)); } @VisibleForTesting - WifiTetherPreferenceController(Context context, Lifecycle lifecycle, - boolean initSoftApManager) { + WifiTetherPreferenceController( + Context context, + Lifecycle lifecycle, + WifiManager wifiManager, + TetheringManager tetheringManager, + boolean initSoftApManager, + boolean isWifiTetheringAllow) { super(context); - mTetheringManager = context.getSystemService(TetheringManager.class); - mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); - mWifiRegexs = mTetheringManager.getTetherableWifiRegexs(); - mIsWifiTetheringAllow = WifiEnterpriseRestrictionUtils.isWifiTetheringAllowed(context); - mLifecycle = lifecycle; + final String[] wifiRegexs = tetheringManager.getTetherableWifiRegexs(); + if (wifiRegexs != null && wifiRegexs.length != 0) { + mIsWifiTetherable = true; + } + + mIsWifiTetheringAllow = isWifiTetheringAllow; + if (!isWifiTetheringAllow) return; + + mWifiManager = wifiManager; + if (lifecycle != null) { lifecycle.addObserver(this); } @@ -81,9 +92,7 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController @Override public boolean isAvailable() { - return mWifiRegexs != null - && mWifiRegexs.length != 0 - && !Utils.isMonkeyRunning(); + return mIsWifiTetherable && !Utils.isMonkeyRunning(); } @Override @@ -94,7 +103,10 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController // unavailable return; } - mPreference.setEnabled(mIsWifiTetheringAllow); + if (!mIsWifiTetheringAllow && mPreference.isEnabled()) { + mPreference.setEnabled(false); + mPreference.setSummary(R.string.not_allowed_by_ent); + } } @Override diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java index 7ce2bf3b32c..e8ee7c3b90e 100644 --- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPreferenceControllerTest.java @@ -19,30 +19,29 @@ package com.android.settings.wifi.tether; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.content.Context; import android.net.TetheringManager; import android.net.wifi.SoftApConfiguration; import android.net.wifi.WifiManager; -import android.os.Bundle; -import android.os.UserManager; -import androidx.lifecycle.LifecycleOwner; import androidx.preference.PreferenceScreen; +import androidx.test.core.app.ApplicationProvider; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settingslib.PrimarySwitchPreference; import com.android.settingslib.core.lifecycle.Lifecycle; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; @@ -56,53 +55,44 @@ public class WifiTetherPreferenceControllerTest { private static final String SSID = "Pixel"; - private Context mContext; + @Rule + public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Spy + Context mContext = ApplicationProvider.getApplicationContext(); + @Mock + private Lifecycle mLifecycle; @Mock private TetheringManager mTetheringManager; @Mock private WifiManager mWifiManager; @Mock - private UserManager mUserManager; - @Mock - private Bundle mBundle; - @Mock private PreferenceScreen mScreen; private SoftApConfiguration mSoftApConfiguration; private WifiTetherPreferenceController mController; - private Lifecycle mLifecycle; - private LifecycleOwner mLifecycleOwner; private PrimarySwitchPreference mPreference; @Before public void setUp() { - MockitoAnnotations.initMocks(this); - - mContext = spy(RuntimeEnvironment.application); - mLifecycleOwner = () -> mLifecycle; - mLifecycle = new Lifecycle(mLifecycleOwner); FakeFeatureFactory.setupForTest(); - mPreference = new PrimarySwitchPreference(RuntimeEnvironment.application); + mPreference = new PrimarySwitchPreference(mContext); when(mContext.getSystemService(Context.TETHERING_SERVICE)).thenReturn(mTetheringManager); when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager); - when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager); - when(mUserManager.getUserRestrictions()).thenReturn(mBundle); when(mScreen.findPreference(anyString())).thenReturn(mPreference); mSoftApConfiguration = new SoftApConfiguration.Builder().setSsid(SSID).build(); when(mWifiManager.getSoftApConfiguration()).thenReturn(mSoftApConfiguration); when(mTetheringManager.getTetherableWifiRegexs()).thenReturn(new String[]{"1", "2"}); - mController = new WifiTetherPreferenceController(mContext, mLifecycle, - false /* initSoftApManager */); - mController.mIsWifiTetheringAllow = true; + mController = new WifiTetherPreferenceController(mContext, mLifecycle, mWifiManager, + mTetheringManager, false /* initSoftApManager */, true /* isWifiTetheringAllow */); mController.displayPreference(mScreen); } @Test public void isAvailable_noTetherRegex_shouldReturnFalse() { when(mTetheringManager.getTetherableWifiRegexs()).thenReturn(new String[]{}); - mController = new WifiTetherPreferenceController(mContext, mLifecycle, - false /* initSoftApManager */); + mController = new WifiTetherPreferenceController(mContext, mLifecycle, mWifiManager, + mTetheringManager, false /* initSoftApManager */, true /* isWifiTetheringAllow */); assertThat(mController.isAvailable()).isFalse(); } @@ -114,16 +104,19 @@ public class WifiTetherPreferenceControllerTest { @Test public void displayPreference_wifiTetheringNotAllowed_shouldDisable() { - mController.mIsWifiTetheringAllow = false; + mController = new WifiTetherPreferenceController(mContext, mLifecycle, mWifiManager, + mTetheringManager, false /* initSoftApManager */, false /* isWifiTetheringAllow */); mController.displayPreference(mScreen); assertThat(mPreference.isEnabled()).isFalse(); + assertThat(mPreference.getSummary()).isEqualTo("Not allowed by your organization"); } @Test public void displayPreference_wifiTetheringAllowed_shouldEnable() { - mController.mIsWifiTetheringAllow = true; + mController = new WifiTetherPreferenceController(mContext, mLifecycle, mWifiManager, + mTetheringManager, false /* initSoftApManager */, true /* isWifiTetheringAllow */); mController.displayPreference(mScreen); From 5e3d75a6f980916e70f779d00a19e62ec69816e4 Mon Sep 17 00:00:00 2001 From: Weng Su Date: Fri, 29 Apr 2022 03:41:56 +0800 Subject: [PATCH 09/13] Show restricted message in Wi-Fi Direct summary - See the result screenshot in b/203168953#comment9 Bug: 203168953 Test: manual test make RunSettingsRoboTests \ ROBOTEST_FILTER=WifiP2PPreferenceControllerTest Change-Id: I87ad6d7554fbb4aa86d4617438fa359c67544812 --- .../settings/wifi/p2p/WifiP2pPreferenceController.java | 6 ++++-- .../settings/wifi/p2p/WifiP2PPreferenceControllerTest.java | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/wifi/p2p/WifiP2pPreferenceController.java b/src/com/android/settings/wifi/p2p/WifiP2pPreferenceController.java index db150dab3b5..5d73fa464f1 100644 --- a/src/com/android/settings/wifi/p2p/WifiP2pPreferenceController.java +++ b/src/com/android/settings/wifi/p2p/WifiP2pPreferenceController.java @@ -15,18 +15,17 @@ */ package com.android.settings.wifi.p2p; -import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.location.LocationManager; import android.net.wifi.WifiManager; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; +import com.android.settings.R; import com.android.settings.core.PreferenceControllerMixin; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.lifecycle.Lifecycle; @@ -70,6 +69,9 @@ public class WifiP2pPreferenceController extends AbstractPreferenceController super.displayPreference(screen); mWifiDirectPref = screen.findPreference(KEY_WIFI_DIRECT); togglePreferences(); + if (!mIsWifiDirectAllow) { + mWifiDirectPref.setSummary(R.string.not_allowed_by_ent); + } } @Override diff --git a/tests/robotests/src/com/android/settings/wifi/p2p/WifiP2PPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/p2p/WifiP2PPreferenceControllerTest.java index a9d5611918d..e292f24e5ce 100644 --- a/tests/robotests/src/com/android/settings/wifi/p2p/WifiP2PPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/wifi/p2p/WifiP2PPreferenceControllerTest.java @@ -22,12 +22,10 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -40,6 +38,7 @@ import androidx.lifecycle.LifecycleOwner; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; +import com.android.settings.R; import com.android.settingslib.core.lifecycle.Lifecycle; import org.junit.Before; @@ -142,6 +141,7 @@ public class WifiP2PPreferenceControllerTest { mController.displayPreference(mScreen); verify(mWifiDirectPreference).setEnabled(false); + verify(mWifiDirectPreference).setSummary(R.string.not_allowed_by_ent); } @Test From 1c3cb4505c1abe58f16f1f00475b5f397617a301 Mon Sep 17 00:00:00 2001 From: ykhung Date: Fri, 29 Apr 2022 10:35:39 +0800 Subject: [PATCH 10/13] Hide running time information for "Android System" entry Hide the running time information for "Android System" entry, since this entry will combine multiple system components together. It will provide incorrect running time information. The getRealUid() method maps many UIDs to Process.SYSTEM_UID(1000), which results in combining all of those UIDs into one "Android System" entry. This is the expected behavior. Bug: 220717612 Test: make RunSettingsRoboTests -j56 ROBOTEST_FILTER="com.android.settings.fuelgauge" Change-Id: I0aece0f1e86c36e55a2b5a966b4da9fdea14c748 --- .../settings/fuelgauge/BatteryAppListPreferenceController.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java index 8fb0d1e0f64..db4cc536579 100644 --- a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java +++ b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java @@ -424,6 +424,9 @@ public class BatteryAppListPreferenceController extends AbstractPreferenceContro @VisibleForTesting void setUsageSummary(Preference preference, BatteryEntry entry) { + if (entry.getUid() == Process.SYSTEM_UID) { + return; + } // Only show summary when usage time is longer than one minute final long usageTimeMs = entry.getTimeInForegroundMs(); if (shouldShowSummary(entry) && usageTimeMs >= DateUtils.MINUTE_IN_MILLIS) { From 0f68faf99669f4654a3797f238520501cc2c7988 Mon Sep 17 00:00:00 2001 From: Bonian Chen Date: Wed, 27 Apr 2022 14:27:04 +0800 Subject: [PATCH 11/13] [Settings] Avoid from crash UI when querying data usage Screen update been requested while querying data usage and response not yet available. This change tried to avoid from updating the UI in this case. Bug: 210664126 Test: local Change-Id: Id055fbd441936a9842b4acc978a894a855165bb7 --- .../android/settings/datausage/DataUsageList.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/com/android/settings/datausage/DataUsageList.java b/src/com/android/settings/datausage/DataUsageList.java index dc945f1fc42..4ee653017e4 100644 --- a/src/com/android/settings/datausage/DataUsageList.java +++ b/src/com/android/settings/datausage/DataUsageList.java @@ -48,6 +48,7 @@ import android.widget.ImageView; import android.widget.Spinner; import androidx.annotation.VisibleForTesting; +import androidx.lifecycle.Lifecycle; import androidx.loader.app.LoaderManager.LoaderCallbacks; import androidx.loader.content.Loader; import androidx.preference.Preference; @@ -500,6 +501,17 @@ public class DataUsageList extends DataUsageBaseFragment + cycle.end + "]"); } + // Avoid from updating UI after #onStop. + if (!getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) { + return; + } + + // Avoid from updating UI when async query still on-going. + // This could happen when a request from #onMobileDataEnabledChange. + if (mCycleData == null) { + return; + } + // update chart to show selected cycle, and update detail data // to match updated sweep bounds. mChart.setNetworkCycleData(mCycleData.get(position)); From a95b12f921cb7eb8eee3904850b147d845d6c78a Mon Sep 17 00:00:00 2001 From: Tsung-Mao Fang Date: Thu, 28 Apr 2022 23:40:44 +0800 Subject: [PATCH 12/13] Remove tab symbol for app pinning strings The pinning description can't align together in certain device. To mitigate the alignment problem, we try to remove the \t character now. Test: visual Fix: 222617691 Change-Id: I35974caf9d6fa316a6e4a0a5200ccab6a80f915b --- res/values/strings.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index f87b489f5d6..57eb740b94f 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -10207,18 +10207,18 @@ When an app is pinned, the pinned app may open other apps and personal data may be accessible. \n\nTo use app pinning: - \t\n1.\tTurn on app pinning - \t\n2.\tOpen Overview - \t\n3.\tTap the app icon at the top of the screen, then tap Pin + \t\n1. Turn on app pinning + \t\n2. Open Overview + \t\n3. Tap the app icon at the top of the screen, then tap Pin When an app is pinned, the pinned app may open other apps and personal data may be accessible. \n\nIf you want to securely share your device with someone, try using a guest user instead. \n\nTo use app pinning: - \t\n1.\tTurn on app pinning - \t\n2.\tOpen Overview - \t\n3.\tTap the app icon at the top of the screen, then tap Pin + \t\n1. Turn on app pinning + \t\n2. Open Overview + \t\n3. Tap the app icon at the top of the screen, then tap Pin From ba943c9b946a429c456b5719247be3b1959d6c6b Mon Sep 17 00:00:00 2001 From: Bonian Chen Date: Thu, 28 Apr 2022 15:14:42 +0800 Subject: [PATCH 13/13] [Settings] Rename title of SIM UI when changed Title of subscription page should align with the SIM card. Bug: 230308627 Test: local Change-Id: Iab9ba25ac8c3faf7d37f97c8f3f3488cbaa2e228 --- .../telephony/MobileNetworkSettings.java | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java index 763962c492f..4eb8a857232 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java +++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java @@ -37,6 +37,7 @@ import androidx.preference.Preference; import com.android.settings.R; import com.android.settings.Settings.MobileNetworkActivity; +import com.android.settings.SettingsActivity; import com.android.settings.datausage.BillingCyclePreferenceController; import com.android.settings.datausage.DataUsageSummaryPreferenceController; import com.android.settings.network.ActiveSubscriptionsListener; @@ -54,6 +55,7 @@ import com.android.settingslib.utils.ThreadUtils; import java.util.Arrays; import java.util.List; +import java.util.function.Consumer; @SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC) public class MobileNetworkSettings extends AbstractMobileNetworkSettings { @@ -275,17 +277,37 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings { Log.d(LOG_TAG, "Callback during onResume()"); return; } + + final SubscriptionInfo subInfo = SubscriptionUtil + .getSubscriptionOrDefault(getContext(), mSubId); + + if (subInfo != null) { + /** + * Update the title when SIM stats got changed + */ + final Consumer renameTitle = activity -> { + if (activity != null && !activity.isFinishing()) { + if (activity instanceof SettingsActivity) { + final CharSequence displayName = SubscriptionUtil + .getUniqueSubscriptionDisplayName(subInfo, activity); + ((SettingsActivity)activity).setTitle(displayName); + } + } + }; + + ThreadUtils.postOnMainThread(() -> renameTitle.accept(getActivity())); + } + mActiveSubscriptionsListenerCount++; if (mActiveSubscriptionsListenerCount != 1) { return; } - if (SubscriptionUtil.getSubscriptionOrDefault(getContext(), mSubId) == null) { - finishFragment(); - return; - } - ThreadUtils.postOnMainThread(() -> { + if (subInfo == null) { + finishFragment(); + return; + } mActiveSubscriptionsListenerCount = 0; redrawPreferenceControllers(); });