From 735a216da31d8440d23fed4355521013ead630f3 Mon Sep 17 00:00:00 2001 From: Pavel Grafov Date: Thu, 18 Mar 2021 22:46:08 +0000 Subject: [PATCH 1/4] Prevent disconnecting admin-configured VPN First, if the VPN is configured by an admin, the preference is disabled and tapping on it will results in a policy disclouser dialog. In addition restriction checks in the dialog also check if the VPN is admin-configured. Bug: 179975048 Test: Manual, setting VPN in profile and primary user and via DPM API. Test: make RunSettingsRoboTests -j Merged-In: Id59d2ac2782e83601bc3093d3a092faea36ff5d9 Change-Id: Id59d2ac2782e83601bc3093d3a092faea36ff5d9 --- .../settings/vpn2/AppDialogFragment.java | 12 ++++++++-- .../android/settings/vpn2/AppPreference.java | 23 +++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/vpn2/AppDialogFragment.java b/src/com/android/settings/vpn2/AppDialogFragment.java index ea9e546a6e5..60c65965a00 100644 --- a/src/com/android/settings/vpn2/AppDialogFragment.java +++ b/src/com/android/settings/vpn2/AppDialogFragment.java @@ -17,6 +17,7 @@ package com.android.settings.vpn2; import android.app.Dialog; +import android.app.admin.DevicePolicyManager; import android.app.settings.SettingsEnums; import android.content.Context; import android.content.DialogInterface; @@ -55,6 +56,7 @@ public class AppDialogFragment extends InstrumentedDialogFragment implements App private UserManager mUserManager; private final IConnectivityManager mService = IConnectivityManager.Stub.asInterface( ServiceManager.getService(Context.CONNECTIVITY_SERVICE)); + private DevicePolicyManager mDevicePolicyManager; @Override public int getMetricsCategory() { @@ -97,7 +99,11 @@ public class AppDialogFragment extends InstrumentedDialogFragment implements App @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + mPackageInfo = getArguments().getParcelable(ARG_PACKAGE); mUserManager = UserManager.get(getContext()); + mDevicePolicyManager = getContext() + .createContextAsUser(UserHandle.of(getUserId()), /* flags= */ 0) + .getSystemService(DevicePolicyManager.class); } @Override @@ -106,7 +112,6 @@ public class AppDialogFragment extends InstrumentedDialogFragment implements App final String label = args.getString(ARG_LABEL); boolean managing = args.getBoolean(ARG_MANAGING); boolean connected = args.getBoolean(ARG_CONNECTED); - mPackageInfo = args.getParcelable(ARG_PACKAGE); if (managing) { return new AppDialog(getActivity(), this, mPackageInfo, label); @@ -178,7 +183,10 @@ public class AppDialogFragment extends InstrumentedDialogFragment implements App private boolean isUiRestricted() { final UserHandle userHandle = UserHandle.of(getUserId()); - return mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN, userHandle); + if (mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN, userHandle)) { + return true; + } + return mPackageInfo.packageName.equals(mDevicePolicyManager.getAlwaysOnVpnPackage()); } private int getUserId() { diff --git a/src/com/android/settings/vpn2/AppPreference.java b/src/com/android/settings/vpn2/AppPreference.java index 6b64250df32..8ee2f5f2d0c 100644 --- a/src/com/android/settings/vpn2/AppPreference.java +++ b/src/com/android/settings/vpn2/AppPreference.java @@ -16,6 +16,7 @@ package com.android.settings.vpn2; +import android.app.admin.DevicePolicyManager; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -26,6 +27,8 @@ import androidx.preference.Preference; import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.VpnConfig; +import com.android.settingslib.RestrictedLockUtils; +import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; /** * {@link androidx.preference.Preference} containing information about a VPN @@ -43,6 +46,7 @@ public class AppPreference extends ManageablePreference { super.setUserId(userId); mPackageName = packageName; + disableIfConfiguredByAdmin(); // Fetch icon and VPN label String label = packageName; @@ -74,6 +78,25 @@ public class AppPreference extends ManageablePreference { setIcon(icon); } + /** + * Disable this preference if VPN is set as always on by a profile or device owner. + * NB: it should be called after super.setUserId() otherwise admin information can be lost. + */ + private void disableIfConfiguredByAdmin() { + if (isDisabledByAdmin()) { + // Already disabled due to user restriction. + return; + } + final DevicePolicyManager dpm = getContext() + .createContextAsUser(UserHandle.of(getUserId()), /* flags= */ 0) + .getSystemService(DevicePolicyManager.class); + if (mPackageName.equals(dpm.getAlwaysOnVpnPackage())) { + final EnforcedAdmin admin = RestrictedLockUtils.getProfileOrDeviceOwner( + getContext(), UserHandle.of(mUserId)); + setDisabledByAdmin(admin); + } + } + public PackageInfo getPackageInfo() { try { PackageManager pm = getUserContext().getPackageManager(); From 7366d71702cc46a4d9d68080528d641c61a75d33 Mon Sep 17 00:00:00 2001 From: "Wesley.CW Wang" Date: Thu, 1 Apr 2021 22:28:50 +0800 Subject: [PATCH 2/4] Fix BatteryInfo test fail - rollback part of ag/14063746 Bug: 183689347 Test: make RunSettingsRoboTests Change-Id: I11129713dac2584c54890f65af53ce76b0672b8d --- .../src/com/android/settings/fuelgauge/BatteryInfoTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java index 8eed2cb3836..afec5cbec3e 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java @@ -150,9 +150,10 @@ public class BatteryInfoTest { mBatteryUsageStats, estimate, SystemClock.elapsedRealtime() * 1000, true /* shortString */); - // Both long and short strings should not have extra text - assertThat(info.remainingLabel.toString()).doesNotContain(ENHANCED_STRING_SUFFIX); + // We only add special mention for the long string + assertThat(info.remainingLabel.toString()).contains(ENHANCED_STRING_SUFFIX); assertThat(info.suggestionLabel).contains(BATTERY_RUN_OUT_PREFIX); + // shortened string should not have extra text assertThat(info2.remainingLabel.toString()).doesNotContain(ENHANCED_STRING_SUFFIX); assertThat(info2.suggestionLabel).contains(BATTERY_RUN_OUT_PREFIX); } From faf44124ba666663886b3d33c366caebafee2518 Mon Sep 17 00:00:00 2001 From: Edward Cunningham Date: Sun, 14 Mar 2021 13:47:00 +0000 Subject: [PATCH 3/4] Improve installer attribution in App Info. When an app is installed by the Package Installer app on behalf of another app (eg. a browser, file manager or app store that opens an APK via an activity start), it is preferable to attribute the install to the originating app rather than the 'Package Installer' itself. Since Android R, package manager keeps track of the necessary install source information which enables this more precise attribution. If an originating package is recorded and was set by a system app, we use this as the user-visible 'installer'. Bug: 182365285 Test: make RunSettingsRoboTests Change-Id: Ibb329d6fe8f0fa2ad51d3530a219b2d8b8d6c17b --- .../settings/applications/AppStoreUtil.java | 30 ++++-- .../applications/AppStoreUtilTest.java | 96 +++++++++++++++++++ ...InstallerInfoPreferenceControllerTest.java | 6 +- ...antAppButtonsPreferenceControllerTest.java | 44 ++++----- 4 files changed, 144 insertions(+), 32 deletions(-) create mode 100644 tests/robotests/src/com/android/settings/applications/AppStoreUtilTest.java diff --git a/src/com/android/settings/applications/AppStoreUtil.java b/src/com/android/settings/applications/AppStoreUtil.java index 13e551692c6..79a4f35e19f 100644 --- a/src/com/android/settings/applications/AppStoreUtil.java +++ b/src/com/android/settings/applications/AppStoreUtil.java @@ -18,6 +18,9 @@ package com.android.settings.applications; import android.content.Context; import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.InstallSourceInfo; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.util.Log; @@ -31,15 +34,30 @@ public class AppStoreUtil { .setClassName(result.activityInfo.packageName, result.activityInfo.name) : null; } - // Returns the package name of the app which installed a given packageName, if one is - // available. + // Returns the package name of the app that we consider to be the user-visible 'installer' + // of given packageName, if one is available. public static String getInstallerPackageName(Context context, String packageName) { - String installerPackageName = null; + String installerPackageName; try { - installerPackageName = - context.getPackageManager().getInstallerPackageName(packageName); - } catch (IllegalArgumentException e) { + InstallSourceInfo source = + context.getPackageManager().getInstallSourceInfo(packageName); + // By default, use the installing package name. + installerPackageName = source.getInstallingPackageName(); + // Use the recorded originating package name only if the initiating package is a system + // app (eg. Package Installer). The originating package is not verified by the platform, + // so we choose to ignore this when supplied by a non-system app. + String originatingPackageName = source.getOriginatingPackageName(); + String initiatingPackageName = source.getInitiatingPackageName(); + if (originatingPackageName != null && initiatingPackageName != null) { + ApplicationInfo ai = context.getPackageManager().getApplicationInfo( + initiatingPackageName, 0); + if ((ai.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + installerPackageName = originatingPackageName; + } + } + } catch (NameNotFoundException e) { Log.e(LOG_TAG, "Exception while retrieving the package installer of " + packageName, e); + installerPackageName = null; } return installerPackageName; } diff --git a/tests/robotests/src/com/android/settings/applications/AppStoreUtilTest.java b/tests/robotests/src/com/android/settings/applications/AppStoreUtilTest.java new file mode 100644 index 00000000000..623e2a2eb0c --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/AppStoreUtilTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2021 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.applications; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.InstallSourceInfo; +import android.content.pm.PackageManager; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +public class AppStoreUtilTest { + + private static final String PACKAGE_NAME = "com.android.app"; + private static final String INSTALLING_PACKAGE_NAME = "com.android.installing"; + private static final String INITIATING_PACKAGE_NAME = "com.android.initiating"; + private static final String ORIGINATING_PACKAGE_NAME = "com.android.originating"; + + @Mock + private PackageManager mPackageManager; + @Mock + private ApplicationInfo mInitiatingAppInfo; + @Mock + private InstallSourceInfo mInstallSourceInfo; + + private Context mContext; + + @Before + public void setUp() throws PackageManager.NameNotFoundException { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application); + when(mContext.getPackageManager()).thenReturn(mPackageManager); + when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(mInstallSourceInfo); + when(mInstallSourceInfo.getInstallingPackageName()).thenReturn(INSTALLING_PACKAGE_NAME); + when(mInstallSourceInfo.getInitiatingPackageName()).thenReturn(INITIATING_PACKAGE_NAME); + when(mInstallSourceInfo.getOriginatingPackageName()).thenReturn(ORIGINATING_PACKAGE_NAME); + when(mPackageManager.getApplicationInfo(eq(INITIATING_PACKAGE_NAME), anyInt())) + .thenReturn(mInitiatingAppInfo); + } + + @Test + public void getInstallerPackageName_hasOriginatingByNonSystem_shouldReturnInstalling() { + assertThat(AppStoreUtil.getInstallerPackageName(mContext, PACKAGE_NAME)) + .isEqualTo(INSTALLING_PACKAGE_NAME); + } + + @Test + public void getInstallerPackageName_hasOriginatingBySystem_shouldReturnOriginating() { + mInitiatingAppInfo.flags = ApplicationInfo.FLAG_SYSTEM; + assertThat(AppStoreUtil.getInstallerPackageName(mContext, PACKAGE_NAME)) + .isEqualTo(ORIGINATING_PACKAGE_NAME); + } + + @Test + public void getInstallerPackageName_noInitiating_shouldReturnInstalling() { + when(mInstallSourceInfo.getInitiatingPackageName()).thenReturn(null); + assertThat(AppStoreUtil.getInstallerPackageName(mContext, PACKAGE_NAME)) + .isEqualTo(INSTALLING_PACKAGE_NAME); + } + + @Test + public void getInstallerPackageName_noOriginating_shouldReturnInstalling() { + when(mInstallSourceInfo.getOriginatingPackageName()).thenReturn(null); + assertThat(AppStoreUtil.getInstallerPackageName(mContext, PACKAGE_NAME)) + .isEqualTo(INSTALLING_PACKAGE_NAME); + } +} diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceControllerTest.java index d37a6850a54..deb5a3f816a 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceControllerTest.java @@ -32,6 +32,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.InstallSourceInfo; import android.content.pm.ModuleInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -60,6 +61,8 @@ public class AppInstallerInfoPreferenceControllerTest { @Mock private ApplicationInfo mAppInfo; @Mock + private InstallSourceInfo mInstallSourceInfo; + @Mock private AppInfoDashboardFragment mFragment; @Mock private Preference mPreference; @@ -74,7 +77,8 @@ public class AppInstallerInfoPreferenceControllerTest { when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); when(mContext.getPackageManager()).thenReturn(mPackageManager); final String installerPackage = "Installer1"; - when(mPackageManager.getInstallerPackageName(anyString())).thenReturn(installerPackage); + when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(mInstallSourceInfo); + when(mInstallSourceInfo.getInstallingPackageName()).thenReturn(installerPackage); when(mPackageManager.getApplicationInfo(eq(installerPackage), anyInt())) .thenReturn(mAppInfo); mController = new AppInstallerInfoPreferenceController(mContext, "test_key"); diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceControllerTest.java index b7b67991778..a4277ddcf62 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/InstantAppButtonsPreferenceControllerTest.java @@ -33,6 +33,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.InstallSourceInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; @@ -87,7 +88,7 @@ public class InstantAppButtonsPreferenceControllerTest { private InstantAppButtonsPreferenceController mController; @Before - public void setUp() { + public void setUp() throws PackageManager.NameNotFoundException { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); when(mContext.getPackageManager()).thenReturn(mPackageManager); @@ -107,6 +108,10 @@ public class InstantAppButtonsPreferenceControllerTest { when(mPreference.getKey()).thenReturn("instant_app_buttons"); mScreen.addPreference(mPreference); when(mPreference.findViewById(R.id.instant_app_button_container)).thenReturn(buttons); + final InstallSourceInfo installSourceInfo = mock(InstallSourceInfo.class); + when(mPackageManager.getInstallSourceInfo(TEST_AIA_PACKAGE_NAME)) + .thenReturn(installSourceInfo); + when(installSourceInfo.getInstallingPackageName()).thenReturn(TEST_INSTALLER_PACKAGE_NAME); } @Test @@ -165,12 +170,7 @@ public class InstantAppButtonsPreferenceControllerTest { @Test public void onPrepareOptionsMenu_hasAppStoreLink_shoulNotDisableInstallInstantAppMenu() { ReflectionHelpers.setField(mController, "mLaunchUri", "www.test.launch"); - final ResolveInfo resolveInfo = mock(ResolveInfo.class); - final ActivityInfo activityInfo = mock(ActivityInfo.class); - resolveInfo.activityInfo = activityInfo; - activityInfo.packageName = TEST_INSTALLER_PACKAGE_NAME; - activityInfo.name = TEST_INSTALLER_ACTIVITY_NAME; - when(mPackageManager.resolveActivity(any(), anyInt())).thenReturn(resolveInfo); + initAppStoreInfo(); final Menu menu = mock(Menu.class); final MenuItem menuItem = mock(MenuItem.class); when(menu.findItem(AppInfoDashboardFragment.INSTALL_INSTANT_APP_MENU)).thenReturn(menuItem); @@ -191,12 +191,7 @@ public class InstantAppButtonsPreferenceControllerTest { @Test public void onOptionsItemSelected_shouldOpenAppStore() { - final ResolveInfo resolveInfo = mock(ResolveInfo.class); - final ActivityInfo activityInfo = mock(ActivityInfo.class); - resolveInfo.activityInfo = activityInfo; - activityInfo.packageName = TEST_INSTALLER_PACKAGE_NAME; - activityInfo.name = TEST_INSTALLER_ACTIVITY_NAME; - when(mPackageManager.resolveActivity(any(), anyInt())).thenReturn(resolveInfo); + initAppStoreInfo(); mController.displayPreference(mScreen); final ComponentName componentName = new ComponentName(TEST_INSTALLER_PACKAGE_NAME, TEST_INSTALLER_ACTIVITY_NAME); @@ -235,12 +230,7 @@ public class InstantAppButtonsPreferenceControllerTest { @Test public void displayPreference_hasAppStoreLink_shoulSetClickListenerForInstallButton() { - final ResolveInfo resolveInfo = mock(ResolveInfo.class); - final ActivityInfo activityInfo = mock(ActivityInfo.class); - resolveInfo.activityInfo = activityInfo; - activityInfo.packageName = TEST_INSTALLER_PACKAGE_NAME; - activityInfo.name = TEST_INSTALLER_ACTIVITY_NAME; - when(mPackageManager.resolveActivity(any(), anyInt())).thenReturn(resolveInfo); + initAppStoreInfo(); mController.displayPreference(mScreen); @@ -272,12 +262,7 @@ public class InstantAppButtonsPreferenceControllerTest { @Test public void clickInstallButton_shouldOpenAppStore() { - final ResolveInfo resolveInfo = mock(ResolveInfo.class); - final ActivityInfo activityInfo = mock(ActivityInfo.class); - resolveInfo.activityInfo = activityInfo; - activityInfo.packageName = TEST_INSTALLER_PACKAGE_NAME; - activityInfo.name = TEST_INSTALLER_ACTIVITY_NAME; - when(mPackageManager.resolveActivity(any(), anyInt())).thenReturn(resolveInfo); + initAppStoreInfo(); mController.displayPreference(mScreen); final ComponentName componentName = new ComponentName(TEST_INSTALLER_PACKAGE_NAME, TEST_INSTALLER_ACTIVITY_NAME); @@ -302,4 +287,13 @@ public class InstantAppButtonsPreferenceControllerTest { verify(fragmentTransaction).add(any(InstantAppButtonDialogFragment.class), eq("instant_app_buttons")); } + + private void initAppStoreInfo() { + final ResolveInfo resolveInfo = mock(ResolveInfo.class); + final ActivityInfo activityInfo = mock(ActivityInfo.class); + resolveInfo.activityInfo = activityInfo; + activityInfo.packageName = TEST_INSTALLER_PACKAGE_NAME; + activityInfo.name = TEST_INSTALLER_ACTIVITY_NAME; + when(mPackageManager.resolveActivity(any(), anyInt())).thenReturn(resolveInfo); + } } From 6c427a181f2b61922af4fcda7fffa67bd0bd7289 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Fri, 2 Apr 2021 10:13:47 -0400 Subject: [PATCH 4/4] Fix incorrect title Test: view screen after rotating device Fixes: 182171355 Change-Id: If4cb2a9495bd9e59a586f64377ca05e3b01bfbe2 --- .../settings/notification/NotificationAccessSettings.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/settings/notification/NotificationAccessSettings.java b/src/com/android/settings/notification/NotificationAccessSettings.java index a642d9c5b33..10954a185e1 100644 --- a/src/com/android/settings/notification/NotificationAccessSettings.java +++ b/src/com/android/settings/notification/NotificationAccessSettings.java @@ -177,7 +177,7 @@ public class NotificationAccessSettings extends EmptyTextSettings { new SubSettingLauncher(getContext()) .setDestination(NotificationAccessDetails.class.getName()) .setSourceMetricsCategory(getMetricsCategory()) - .setTitleRes(R.string.manage_zen_access_title) + .setTitleRes(R.string.manage_notification_access_title) .setArguments(args) .setExtras(extras) .setUserHandle(UserHandle.getUserHandleForUid(service.applicationInfo.uid))