diff --git a/res/values/strings.xml b/res/values/strings.xml index 5038177eb46..3f9135cc262 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -8007,6 +8007,26 @@ Your most recent security log Never + + + %d app installed by your admin + %d apps installed by your admin + + + + %d app allowed access to your location by your admin + %d apps allowed access to your location by your admin + + + + %d app allowed access to your microphone by your admin + %d apps allowed access to your microphone by your admin + + + + %d app allowed access to your camera by your admin + %d apps allowed access to your camera by your admin + Always-on VPN turned on @@ -8015,11 +8035,6 @@ Always-on VPN turned on in your work profile Global HTTP proxy set - - - %d app installed by your admin - %d apps installed by your admin - Photos & Videos diff --git a/res/xml/enterprise_privacy_settings.xml b/res/xml/enterprise_privacy_settings.xml index 9fed938c138..4bb2a8c746d 100644 --- a/res/xml/enterprise_privacy_settings.xml +++ b/res/xml/enterprise_privacy_settings.xml @@ -59,6 +59,22 @@ + + + + - diff --git a/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounter.java b/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounter.java new file mode 100644 index 00000000000..331d3841b3c --- /dev/null +++ b/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounter.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2017 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 android.app.admin.DevicePolicyManager; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageManager; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.RemoteException; +import android.os.UserHandle; + +import com.android.settings.enterprise.DevicePolicyManagerWrapper; + +/** + * Counts installed apps across all users that have been granted one or more specific permissions by + * the admin. + */ +public abstract class AppWithAdminGrantedPermissionsCounter extends AppCounter { + + private final String[] mPermissions; + private final PackageManagerWrapper mPackageManager; + private final IPackageManager mPackageManagerService; + private final DevicePolicyManagerWrapper mDevicePolicyManager; + + public AppWithAdminGrantedPermissionsCounter(Context context, String[] permissions, + PackageManagerWrapper packageManager, IPackageManager packageManagerService, + DevicePolicyManagerWrapper devicePolicyManager) { + super(context, packageManager); + mPermissions = permissions; + mPackageManager = packageManager; + mPackageManagerService = packageManagerService; + mDevicePolicyManager = devicePolicyManager; + } + + @Override + protected boolean includeInCount(ApplicationInfo info) { + if (info.targetSdkVersion >= Build.VERSION_CODES.M) { + // The app uses run-time permissions. Check whether one or more of the permissions were + // granted by enterprise policy. + for (final String permission : mPermissions) { + if (mDevicePolicyManager.getPermissionGrantState(null /* admin */, info.packageName, + permission) == DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED) { + return true; + } + } + return false; + } + + // The app uses install-time permissions. Check whether the app requested one or more of the + // permissions and was installed by enterprise policy, implicitly granting permissions. + if (mPackageManager.getInstallReason(info.packageName, + new UserHandle(UserHandle.getUserId(info.uid))) + != PackageManager.INSTALL_REASON_POLICY) { + return false; + } + try { + for (final String permission : mPermissions) { + if (mPackageManagerService.checkUidPermission(permission, info.uid) + == PackageManager.PERMISSION_GRANTED) { + return true; + } + } + } catch (RemoteException exception) { + } + return false; + } +} diff --git a/src/com/android/settings/applications/ApplicationFeatureProvider.java b/src/com/android/settings/applications/ApplicationFeatureProvider.java index 1db33a6ef01..101ae915bd2 100644 --- a/src/com/android/settings/applications/ApplicationFeatureProvider.java +++ b/src/com/android/settings/applications/ApplicationFeatureProvider.java @@ -35,17 +35,29 @@ public interface ApplicationFeatureProvider { * Asynchronously calculates the total number of apps installed on the device, across all users * and managed profiles. * - * @param installReason Only consider packages with this install reason; may be any install - * reason defined in {@link android.content.pm.PackageManager} or - * {@link #IGNORE_INSTALL_REASON} to count all packages, irrespective of install reason. + * @param installReason Only consider apps with this install reason; may be any install reason + * defined in {@link android.content.pm.PackageManager} or + * {@link #IGNORE_INSTALL_REASON} to count all apps, irrespective of install reason. * @param callback The callback to invoke with the result */ - void calculateNumberOfInstalledApps(int installReason, NumberOfInstalledAppsCallback callback); + void calculateNumberOfInstalledApps(int installReason, NumberOfAppsCallback callback); /** - * Callback that receives the total number of packages installed on the device. + * Asynchronously calculates the total number of apps installed on the device, across all users + * and managed profiles, that have been granted one or more of the given permissions by the + * admin. + * + * @param permissions Only consider apps that have been granted one or more of these permissions + * by the admin, either at run-time or install-time + * @param callback The callback to invoke with the result */ - interface NumberOfInstalledAppsCallback { - void onNumberOfInstalledAppsResult(int num); + void calculateNumberOfAppsWithAdminGrantedPermissions(String[] permissions, + NumberOfAppsCallback callback); + + /** + * Callback that receives the number of packages installed on the device. + */ + interface NumberOfAppsCallback { + void onNumberOfAppsResult(int num); } } diff --git a/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java b/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java index 902008cd00a..ff7f0f0db01 100644 --- a/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java +++ b/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java @@ -18,21 +18,29 @@ package com.android.settings.applications; import android.app.Fragment; import android.content.Context; +import android.content.pm.IPackageManager; import android.content.pm.UserInfo; import android.os.UserManager; import android.view.View; +import com.android.settings.enterprise.DevicePolicyManagerWrapper; + import java.util.List; public class ApplicationFeatureProviderImpl implements ApplicationFeatureProvider { private final Context mContext; private final PackageManagerWrapper mPm; + private final IPackageManager mPms; + private final DevicePolicyManagerWrapper mDpm; private final UserManager mUm; - public ApplicationFeatureProviderImpl(Context context, PackageManagerWrapper pm) { + public ApplicationFeatureProviderImpl(Context context, PackageManagerWrapper pm, + IPackageManager pms, DevicePolicyManagerWrapper dpm) { mContext = context.getApplicationContext(); mPm = pm; + mPms = pms; + mDpm = dpm; mUm = UserManager.get(mContext); } @@ -42,22 +50,51 @@ public class ApplicationFeatureProviderImpl implements ApplicationFeatureProvide } @Override - public void calculateNumberOfInstalledApps(int installReason, - NumberOfInstalledAppsCallback callback) { - new AllUserInstalledAppCounter(installReason, callback).execute(); + public void calculateNumberOfInstalledApps(int installReason, NumberOfAppsCallback callback) { + new AllUserInstalledAppCounter(mContext, installReason, mPm, callback).execute(); } - private class AllUserInstalledAppCounter extends InstalledAppCounter { - private NumberOfInstalledAppsCallback mCallback; + @Override + public void calculateNumberOfAppsWithAdminGrantedPermissions(String[] permissions, + NumberOfAppsCallback callback) { + new AllUserAppWithAdminGrantedPermissionsCounter(mContext, permissions, mPm, mPms, mDpm, + callback).execute(); + } - AllUserInstalledAppCounter(int installReason, NumberOfInstalledAppsCallback callback) { - super(mContext, installReason, ApplicationFeatureProviderImpl.this.mPm); + private static class AllUserInstalledAppCounter extends InstalledAppCounter { + private NumberOfAppsCallback mCallback; + + AllUserInstalledAppCounter(Context context, int installReason, + PackageManagerWrapper packageManager, NumberOfAppsCallback callback) { + super(context, installReason, packageManager); mCallback = callback; } @Override protected void onCountComplete(int num) { - mCallback.onNumberOfInstalledAppsResult(num); + mCallback.onNumberOfAppsResult(num); + } + + @Override + protected List getUsersToCount() { + return mUm.getUsers(true /* excludeDying */); + } + } + + private static class AllUserAppWithAdminGrantedPermissionsCounter extends + AppWithAdminGrantedPermissionsCounter { + private NumberOfAppsCallback mCallback; + + AllUserAppWithAdminGrantedPermissionsCounter(Context context, String[] permissions, + PackageManagerWrapper packageManager, IPackageManager packageManagerService, + DevicePolicyManagerWrapper devicePolicyManager, NumberOfAppsCallback callback) { + super(context, permissions, packageManager, packageManagerService, devicePolicyManager); + mCallback = callback; + } + + @Override + protected void onCountComplete(int num) { + mCallback.onNumberOfAppsResult(num); } @Override diff --git a/src/com/android/settings/enterprise/AdminActionPreferenceControllerBase.java b/src/com/android/settings/enterprise/AdminActionPreferenceControllerBase.java index 99ff62e2af4..ef03cfb3292 100644 --- a/src/com/android/settings/enterprise/AdminActionPreferenceControllerBase.java +++ b/src/com/android/settings/enterprise/AdminActionPreferenceControllerBase.java @@ -11,6 +11,7 @@ * KIND, either express or implied. See the License for the specific language governing * permissions and limitations under the License. */ + package com.android.settings.enterprise; import android.content.Context; diff --git a/src/com/android/settings/enterprise/AdminGrantedCameraPermissionPreferenceController.java b/src/com/android/settings/enterprise/AdminGrantedCameraPermissionPreferenceController.java new file mode 100644 index 00000000000..a2137ffee6d --- /dev/null +++ b/src/com/android/settings/enterprise/AdminGrantedCameraPermissionPreferenceController.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2017 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.enterprise; + +import android.Manifest; +import android.content.Context; + +import com.android.settings.R; + +public class AdminGrantedCameraPermissionPreferenceController extends + AdminGrantedPermissionsPreferenceControllerBase { + + private static final String KEY_ENTERPRISE_PRIVACY_NUMBER_CAMERA_ACCESS_PACKAGES + = "enterprise_privacy_number_camera_access_packages"; + + public AdminGrantedCameraPermissionPreferenceController(Context context) { + super(context, new String[] {Manifest.permission.CAMERA}, + R.plurals.enterprise_privacy_number_camera_access_packages); + } + + @Override + public String getPreferenceKey() { + return KEY_ENTERPRISE_PRIVACY_NUMBER_CAMERA_ACCESS_PACKAGES; + } +} diff --git a/src/com/android/settings/enterprise/AdminGrantedLocationPermissionsPreferenceController.java b/src/com/android/settings/enterprise/AdminGrantedLocationPermissionsPreferenceController.java new file mode 100644 index 00000000000..0453b53f273 --- /dev/null +++ b/src/com/android/settings/enterprise/AdminGrantedLocationPermissionsPreferenceController.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2017 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.enterprise; + +import android.Manifest; +import android.content.Context; + +import com.android.settings.R; + +public class AdminGrantedLocationPermissionsPreferenceController extends + AdminGrantedPermissionsPreferenceControllerBase { + + private static final String KEY_ENTERPRISE_PRIVACY_NUMBER_LOCATION_ACCESS_PACKAGES + = "enterprise_privacy_number_location_access_packages"; + + public AdminGrantedLocationPermissionsPreferenceController(Context context) { + super(context, new String[] {Manifest.permission.ACCESS_COARSE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION}, + R.plurals.enterprise_privacy_number_location_access_packages); + } + + @Override + public String getPreferenceKey() { + return KEY_ENTERPRISE_PRIVACY_NUMBER_LOCATION_ACCESS_PACKAGES; + } +} diff --git a/src/com/android/settings/enterprise/AdminGrantedMicrophonePermissionPreferenceController.java b/src/com/android/settings/enterprise/AdminGrantedMicrophonePermissionPreferenceController.java new file mode 100644 index 00000000000..3adde92bfc9 --- /dev/null +++ b/src/com/android/settings/enterprise/AdminGrantedMicrophonePermissionPreferenceController.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2017 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.enterprise; + +import android.Manifest; +import android.content.Context; + +import com.android.settings.R; + +public class AdminGrantedMicrophonePermissionPreferenceController extends + AdminGrantedPermissionsPreferenceControllerBase { + + private static final String KEY_ENTERPRISE_PRIVACY_NUMBER_MICROPHONE_ACCESS_PACKAGES + = "enterprise_privacy_number_microphone_access_packages"; + + public AdminGrantedMicrophonePermissionPreferenceController(Context context) { + super(context, new String[] {Manifest.permission.RECORD_AUDIO}, + R.plurals.enterprise_privacy_number_microphone_access_packages); + } + + @Override + public String getPreferenceKey() { + return KEY_ENTERPRISE_PRIVACY_NUMBER_MICROPHONE_ACCESS_PACKAGES; + } +} diff --git a/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerBase.java b/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerBase.java new file mode 100644 index 00000000000..2ca5451211b --- /dev/null +++ b/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerBase.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2017 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.enterprise; + +import android.content.Context; +import android.content.res.Resources; +import android.support.v7.preference.Preference; + +import com.android.settings.applications.ApplicationFeatureProvider; +import com.android.settings.core.PreferenceController; +import com.android.settings.overlay.FeatureFactory; + +public abstract class AdminGrantedPermissionsPreferenceControllerBase extends PreferenceController { + + private final String[] mPermissions; + private final int mStringResourceId; + private final ApplicationFeatureProvider mFeatureProvider; + + public AdminGrantedPermissionsPreferenceControllerBase(Context context, String[] permissions, + int stringResourceId) { + super(context); + mPermissions = permissions; + mStringResourceId = stringResourceId; + mFeatureProvider = FeatureFactory.getFactory(context) + .getApplicationFeatureProvider(context); + } + + @Override + public void updateState(Preference preference) { + mFeatureProvider.calculateNumberOfAppsWithAdminGrantedPermissions(mPermissions, + (num) -> { + if (num == 0) { + preference.setVisible(false); + } else { + preference.setVisible(true); + preference.setTitle(mContext.getResources().getQuantityString( + mStringResourceId, num, num)); + } + }); + } + + @Override + public boolean isAvailable() { + return true; + } +} diff --git a/src/com/android/settings/enterprise/DevicePolicyManagerWrapper.java b/src/com/android/settings/enterprise/DevicePolicyManagerWrapper.java index d65292f56d2..a23448ebd43 100644 --- a/src/com/android/settings/enterprise/DevicePolicyManagerWrapper.java +++ b/src/com/android/settings/enterprise/DevicePolicyManagerWrapper.java @@ -17,6 +17,7 @@ package com.android.settings.enterprise; import android.content.ComponentName; +import android.support.annotation.Nullable; /** * This interface replicates a subset of the android.app.admin.DevicePolicyManager (DPM). The @@ -32,6 +33,14 @@ public interface DevicePolicyManagerWrapper { */ ComponentName getDeviceOwnerComponentOnAnyUser(); + /** + * Calls {@code DevicePolicyManager.getPermissionGrantState()}. + * + * @see android.app.admin.DevicePolicyManager#getPermissionGrantState + */ + int getPermissionGrantState(@Nullable ComponentName admin, String packageName, + String permission); + /** * Calls {@code DevicePolicyManager.getLastSecurityLogRetrievalTime()}. * diff --git a/src/com/android/settings/enterprise/DevicePolicyManagerWrapperImpl.java b/src/com/android/settings/enterprise/DevicePolicyManagerWrapperImpl.java index 710e5ace31a..d122ec645ef 100644 --- a/src/com/android/settings/enterprise/DevicePolicyManagerWrapperImpl.java +++ b/src/com/android/settings/enterprise/DevicePolicyManagerWrapperImpl.java @@ -18,6 +18,7 @@ package com.android.settings.enterprise; import android.app.admin.DevicePolicyManager; import android.content.ComponentName; +import android.support.annotation.Nullable; public class DevicePolicyManagerWrapperImpl implements DevicePolicyManagerWrapper { private final DevicePolicyManager mDpm; @@ -31,6 +32,12 @@ public class DevicePolicyManagerWrapperImpl implements DevicePolicyManagerWrappe return mDpm.getDeviceOwnerComponentOnAnyUser(); } + @Override + public int getPermissionGrantState(@Nullable ComponentName admin, String packageName, + String permission) { + return mDpm.getPermissionGrantState(admin, packageName, permission); + } + @Override public long getLastSecurityLogRetrievalTime() { return mDpm.getLastSecurityLogRetrievalTime(); diff --git a/src/com/android/settings/enterprise/EnterprisePrivacySettings.java b/src/com/android/settings/enterprise/EnterprisePrivacySettings.java index 2cd28629a6f..208bf0cd519 100644 --- a/src/com/android/settings/enterprise/EnterprisePrivacySettings.java +++ b/src/com/android/settings/enterprise/EnterprisePrivacySettings.java @@ -19,8 +19,8 @@ package com.android.settings.enterprise; import android.content.Context; import android.provider.SearchIndexableResource; -import com.android.settings.R; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.settings.R; import com.android.settings.core.PreferenceController; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.search.BaseSearchIndexProvider; @@ -61,10 +61,13 @@ public class EnterprisePrivacySettings extends DashboardFragment { controllers.add(new NetworkLogsPreferenceController(context)); controllers.add(new BugReportsPreferenceController(context)); controllers.add(new SecurityLogsPreferenceController(context)); + controllers.add(new EnterpriseInstalledPackagesPreferenceController(context)); + controllers.add(new AdminGrantedLocationPermissionsPreferenceController(context)); + controllers.add(new AdminGrantedMicrophonePermissionPreferenceController(context)); + controllers.add(new AdminGrantedCameraPermissionPreferenceController(context)); controllers.add(new AlwaysOnVpnPrimaryUserPreferenceController(context)); controllers.add(new AlwaysOnVpnManagedProfilePreferenceController(context)); controllers.add(new GlobalHttpProxyPreferenceController(context)); - controllers.add(new EnterpriseInstalledPackagesPreferenceController(context)); return controllers; } diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.java b/src/com/android/settings/overlay/FeatureFactoryImpl.java index 060b58c158e..18fcaafaa1e 100644 --- a/src/com/android/settings/overlay/FeatureFactoryImpl.java +++ b/src/com/android/settings/overlay/FeatureFactoryImpl.java @@ -16,6 +16,7 @@ package com.android.settings.overlay; +import android.app.AppGlobals; import android.app.admin.DevicePolicyManager; import android.content.Context; import android.net.ConnectivityManager; @@ -85,7 +86,10 @@ public class FeatureFactoryImpl extends FeatureFactory { public ApplicationFeatureProvider getApplicationFeatureProvider(Context context) { if (mApplicationFeatureProvider == null) { mApplicationFeatureProvider = new ApplicationFeatureProviderImpl(context, - new PackageManagerWrapperImpl(context.getPackageManager())); + new PackageManagerWrapperImpl(context.getPackageManager()), + AppGlobals.getPackageManager(), + new DevicePolicyManagerWrapperImpl((DevicePolicyManager) context + .getSystemService(Context.DEVICE_POLICY_SERVICE))); } return mApplicationFeatureProvider; } diff --git a/tests/robotests/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounterTest.java b/tests/robotests/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounterTest.java new file mode 100644 index 00000000000..9fc416d4fef --- /dev/null +++ b/tests/robotests/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounterTest.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2017 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 android.app.admin.DevicePolicyManager; +import android.content.Context; +import android.content.pm.IPackageManager; +import android.content.pm.PackageManager; +import android.content.pm.UserInfo; +import android.os.Build; +import android.os.UserHandle; + +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; +import com.android.settings.enterprise.DevicePolicyManagerWrapper; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowApplication; + +import java.util.Arrays; +import java.util.List; + +import static com.android.settings.testutils.ApplicationTestUtils.buildInfo; +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +/** + * Tests for {@link InstalledAppCounter}. + */ +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public final class AppWithAdminGrantedPermissionsCounterTest { + + private final String APP_1 = "app1"; + private final String APP_2 = "app2"; + private final String APP_3 = "app3"; + private final String APP_4 = "app4"; + private final String APP_5 = "app5"; + private final String APP_6 = "app6"; + + private final int MAIN_USER_ID = 0; + private final int MANAGED_PROFILE_ID = 10; + + private final int PER_USER_UID_RANGE = 100000; + private final int APP_1_UID = MAIN_USER_ID * PER_USER_UID_RANGE + 1; + private final int APP_2_UID = MAIN_USER_ID * PER_USER_UID_RANGE + 2; + private final int APP_3_UID = MAIN_USER_ID * PER_USER_UID_RANGE + 3; + private final int APP_4_UID = MAIN_USER_ID * PER_USER_UID_RANGE + 4; + private final int APP_5_UID = MAIN_USER_ID * PER_USER_UID_RANGE + 5; + private final int APP_6_UID = MANAGED_PROFILE_ID * PER_USER_UID_RANGE + 1; + + private final String PERMISSION_1 = "some.permission.1"; + private final String PERMISSION_2 = "some.permission.2"; + private final String[] PERMISSIONS = {PERMISSION_1, PERMISSION_2}; + + @Mock private Context mContext; + @Mock private PackageManagerWrapper mPackageManager; + @Mock private IPackageManager mPackageManagerService; + @Mock private DevicePolicyManagerWrapper mDevicePolicyManager; + private List mUsersToCount; + + private int mAppCount = -1; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testCountInstalledAppsAcrossAllUsers() throws Exception { + // There are two users. + mUsersToCount = Arrays.asList( + new UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_ADMIN), + new UserInfo(MANAGED_PROFILE_ID, "managed profile", 0)); + + // The first user has five apps installed: + // * app1 uses run-time permissions. It has been granted one of the permissions by the + // admin. It should be counted. + // * app2 uses run-time permissions. It has not been granted any of the permissions by the + // admin. It should not be counted. + // * app3 uses install-time permissions. It was installed by the admin and requested one of + // the permissions. It should be counted. + // * app4 uses install-time permissions. It was not installed by the admin but did request + // one of the permissions. It should not be counted. + // * app5 uses install-time permissions. It was installed by the admin but did not request + // any of the permissions. It should not be counted. + when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS + | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS + | PackageManager.MATCH_ANY_USER, + MAIN_USER_ID)).thenReturn(Arrays.asList( + buildInfo(APP_1_UID, APP_1, 0 /* flags */, Build.VERSION_CODES.M), + buildInfo(APP_2_UID, APP_2, 0 /* flags */, Build.VERSION_CODES.M), + buildInfo(APP_3_UID, APP_3, 0 /* flags */, Build.VERSION_CODES.LOLLIPOP), + buildInfo(APP_4_UID, APP_4, 0 /* flags */, Build.VERSION_CODES.LOLLIPOP), + buildInfo(APP_5_UID, APP_5, 0 /* flags */, Build.VERSION_CODES.LOLLIPOP))); + + // Grant run-time permissions as appropriate. + when(mDevicePolicyManager.getPermissionGrantState(null, APP_1, PERMISSION_1)) + .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED); + when(mDevicePolicyManager.getPermissionGrantState(null, APP_1, PERMISSION_2)) + .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED); + when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_2), anyObject())) + .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED); + when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_3), anyObject())) + .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED); + when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_4), anyObject())) + .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED); + when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_5), anyObject())) + .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED); + + // Grant install-time permissions as appropriate. + when(mPackageManagerService.checkUidPermission(anyObject(), eq(APP_1_UID))) + .thenReturn(PackageManager.PERMISSION_DENIED); + when(mPackageManagerService.checkUidPermission(anyObject(), eq(APP_2_UID))) + .thenReturn(PackageManager.PERMISSION_DENIED); + when(mPackageManagerService.checkUidPermission(PERMISSION_1, APP_3_UID)) + .thenReturn(PackageManager.PERMISSION_DENIED); + when(mPackageManagerService.checkUidPermission(PERMISSION_2, APP_3_UID)) + .thenReturn(PackageManager.PERMISSION_GRANTED); + when(mPackageManagerService.checkUidPermission(PERMISSION_1, APP_4_UID)) + .thenReturn(PackageManager.PERMISSION_DENIED); + when(mPackageManagerService.checkUidPermission(PERMISSION_2, APP_4_UID)) + .thenReturn(PackageManager.PERMISSION_GRANTED); + when(mPackageManagerService.checkUidPermission(anyObject(), eq(APP_5_UID))) + .thenReturn(PackageManager.PERMISSION_DENIED); + + // app3 and app5 were installed by enterprise policy. + final UserHandle mainUser = new UserHandle(MAIN_USER_ID); + when(mPackageManager.getInstallReason(APP_1, mainUser)) + .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN); + when(mPackageManager.getInstallReason(APP_2, mainUser)) + .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN); + when(mPackageManager.getInstallReason(APP_3, mainUser)) + .thenReturn(PackageManager.INSTALL_REASON_POLICY); + when(mPackageManager.getInstallReason(APP_4, mainUser)) + .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN); + when(mPackageManager.getInstallReason(APP_5, mainUser)) + .thenReturn(PackageManager.INSTALL_REASON_POLICY); + + // The second user has one app installed. This app uses run-time permissions. It has been + // granted both permissions by the admin. It should be counted. + when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS + | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, + MANAGED_PROFILE_ID)).thenReturn(Arrays.asList( + buildInfo(APP_6_UID, APP_6, 0 /* flags */, Build.VERSION_CODES.M))); + + // Grant run-time permissions as appropriate. + when(mDevicePolicyManager.getPermissionGrantState(eq(null), eq(APP_6), anyObject())) + .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED); + + // Grant install-time permissions as appropriate. + when(mPackageManagerService.checkUidPermission(anyObject(), eq(APP_6_UID))) + .thenReturn(PackageManager.PERMISSION_DENIED); + + // app6 was not installed by enterprise policy. + final UserHandle managedProfileUser = new UserHandle(MANAGED_PROFILE_ID); + when(mPackageManager.getInstallReason(APP_6, managedProfileUser)) + .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN); + + // Count the number of all apps installed that were granted on or more permissions by the + // admin. Wait for the background task to finish. + (new AppWithAdminGrantedPermissionsCounterTestable(PERMISSIONS)).execute(); + ShadowApplication.runBackgroundTasks(); + + assertThat(mAppCount).isEqualTo(3); + + // Verify that installed packages were retrieved for the users returned by + // InstalledAppCounterTestable.getUsersToCount() only. + verify(mPackageManager).getInstalledApplicationsAsUser(anyInt(), eq(MAIN_USER_ID)); + verify(mPackageManager).getInstalledApplicationsAsUser(anyInt(), + eq(MANAGED_PROFILE_ID)); + verify(mPackageManager, atLeast(0)).getInstallReason(anyObject(), anyObject()); + verifyNoMoreInteractions(mPackageManager); + + } + + private class AppWithAdminGrantedPermissionsCounterTestable extends + AppWithAdminGrantedPermissionsCounter { + public AppWithAdminGrantedPermissionsCounterTestable(String[] permissions) { + super(mContext, permissions, mPackageManager, mPackageManagerService, + mDevicePolicyManager); + } + + @Override + protected void onCountComplete(int num) { + mAppCount = num; + } + + @Override + protected List getUsersToCount() { + return mUsersToCount; + } + } +} diff --git a/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java index 615d9adbffe..aba4a125259 100644 --- a/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java +++ b/tests/robotests/src/com/android/settings/applications/ApplicationFeatureProviderImplTest.java @@ -16,14 +16,18 @@ package com.android.settings.applications; +import android.app.admin.DevicePolicyManager; import android.content.Context; +import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.UserInfo; +import android.os.Build; import android.os.UserHandle; import android.os.UserManager; import com.android.settings.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; +import com.android.settings.enterprise.DevicePolicyManagerWrapper; import com.android.settings.testutils.ApplicationTestUtils; import com.android.settings.testutils.shadow.ShadowUserManager; import org.junit.Before; @@ -50,15 +54,25 @@ public final class ApplicationFeatureProviderImplTest { private final int MAIN_USER_ID = 0; private final int MANAGED_PROFILE_ID = 10; + private final int PER_USER_UID_RANGE = 100000; + private final int APP_1_UID = MAIN_USER_ID * PER_USER_UID_RANGE + 1; + private final int APP_2_UID = MANAGED_PROFILE_ID * PER_USER_UID_RANGE + 1; + private final String APP_1 = "app1"; private final String APP_2 = "app2"; + private final String PERMISSION = "some.permission"; + private @Mock UserManager mUserManager; private @Mock Context mContext; private @Mock PackageManagerWrapper mPackageManager; + @Mock private IPackageManager mPackageManagerService; + @Mock private DevicePolicyManagerWrapper mDevicePolicyManager; private ApplicationFeatureProvider mProvider; + private int mAppCount = -1; + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -66,14 +80,66 @@ public final class ApplicationFeatureProviderImplTest { when(mContext.getApplicationContext()).thenReturn(mContext); when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); - mProvider = new ApplicationFeatureProviderImpl(mContext, mPackageManager); + mProvider = new ApplicationFeatureProviderImpl(mContext, mPackageManager, + mPackageManagerService, mDevicePolicyManager); } @Test public void testCalculateNumberOfInstalledApps() { - final Integer[] numberOfInstalledApps = new Integer[1]; - numberOfInstalledApps[0] = null; + setUpUsersAndInstalledApps(); + when(mPackageManager.getInstallReason(APP_1, new UserHandle(MAIN_USER_ID))) + .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN); + when(mPackageManager.getInstallReason(APP_2, new UserHandle(MANAGED_PROFILE_ID))) + .thenReturn(PackageManager.INSTALL_REASON_POLICY); + + // Count all installed apps. + mAppCount = -1; + mProvider.calculateNumberOfInstalledApps(ApplicationFeatureProvider.IGNORE_INSTALL_REASON, + (num) -> { + mAppCount = num; + }); + ShadowApplication.runBackgroundTasks(); + assertThat(mAppCount).isEqualTo(2); + + // Count apps with specific install reason only. + mAppCount = -1; + mProvider.calculateNumberOfInstalledApps(PackageManager.INSTALL_REASON_POLICY, + (num) -> { + mAppCount = num; + }); + ShadowApplication.runBackgroundTasks(); + assertThat(mAppCount).isEqualTo(1); + } + + @Test + public void testCalculateNumberOfAppsWithAdminGrantedPermissions() throws Exception { + setUpUsersAndInstalledApps(); + + when(mDevicePolicyManager.getPermissionGrantState(null, APP_1, PERMISSION)) + .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED); + when(mDevicePolicyManager.getPermissionGrantState(null, APP_2, PERMISSION)) + .thenReturn(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED); + when(mPackageManagerService.checkUidPermission(PERMISSION, APP_1_UID)) + .thenReturn(PackageManager.PERMISSION_DENIED); + when(mPackageManagerService.checkUidPermission(PERMISSION, APP_2_UID)) + .thenReturn(PackageManager.PERMISSION_GRANTED); + when(mPackageManager.getInstallReason(APP_1, new UserHandle(MAIN_USER_ID))) + .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN); + when(mPackageManager.getInstallReason(APP_2, new UserHandle(MANAGED_PROFILE_ID))) + .thenReturn(PackageManager.INSTALL_REASON_POLICY); + + mAppCount = -1; + mProvider.calculateNumberOfAppsWithAdminGrantedPermissions(new String[] {PERMISSION}, + (num) -> { + mAppCount = num; + }); + ShadowApplication.runBackgroundTasks(); + assertThat(mAppCount).isEqualTo(2); + + } + + private void setUpUsersAndInstalledApps() { when(mUserManager.getUsers(true)).thenReturn(Arrays.asList( new UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_ADMIN), new UserInfo(MANAGED_PROFILE_ID, "managed profile", 0))); @@ -82,32 +148,12 @@ public final class ApplicationFeatureProviderImplTest { | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS | PackageManager.MATCH_ANY_USER, MAIN_USER_ID)).thenReturn(Arrays.asList( - ApplicationTestUtils.buildInfo(MAIN_USER_ID, APP_1, 0 /* flags */))); - when(mPackageManager.getInstallReason(APP_1, new UserHandle(MAIN_USER_ID))) - .thenReturn(PackageManager.INSTALL_REASON_UNKNOWN); - + ApplicationTestUtils.buildInfo(APP_1_UID, APP_1, 0 /* flags */, + Build.VERSION_CODES.M))); when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, MANAGED_PROFILE_ID)).thenReturn(Arrays.asList( - ApplicationTestUtils.buildInfo(MANAGED_PROFILE_ID, APP_2, 0 /* flags */))); - when(mPackageManager.getInstallReason(APP_2, new UserHandle(MANAGED_PROFILE_ID))) - .thenReturn(PackageManager.INSTALL_REASON_POLICY); - - // Count all installed apps. - mProvider.calculateNumberOfInstalledApps(ApplicationFeatureProvider.IGNORE_INSTALL_REASON, - (num) -> { - numberOfInstalledApps[0] = num; - }); - ShadowApplication.runBackgroundTasks(); - assertThat(numberOfInstalledApps[0]).isEqualTo(2); - - // Count apps with specific install reason only. - numberOfInstalledApps[0] = null; - mProvider.calculateNumberOfInstalledApps(PackageManager.INSTALL_REASON_POLICY, - (num) -> { - numberOfInstalledApps[0] = num; - }); - ShadowApplication.runBackgroundTasks(); - assertThat(numberOfInstalledApps[0]).isEqualTo(1); + ApplicationTestUtils.buildInfo(APP_2_UID, APP_2, 0 /* flags */, + Build.VERSION_CODES.LOLLIPOP))); } } diff --git a/tests/robotests/src/com/android/settings/applications/InstalledAppCounterTest.java b/tests/robotests/src/com/android/settings/applications/InstalledAppCounterTest.java index 3dc2e9b0481..8495603482f 100644 --- a/tests/robotests/src/com/android/settings/applications/InstalledAppCounterTest.java +++ b/tests/robotests/src/com/android/settings/applications/InstalledAppCounterTest.java @@ -71,6 +71,10 @@ public final class InstalledAppCounterTest { private final int MAIN_USER_ID = 0; private final int MANAGED_PROFILE_ID = 10; + private final int PER_USER_UID_RANGE = 100000; + private final int MAIN_USER_APP_UID = MAIN_USER_ID * PER_USER_UID_RANGE; + private final int MANAGED_PROFILE_APP_UID = MANAGED_PROFILE_ID * PER_USER_UID_RANGE; + @Mock private UserManager mUserManager; @Mock private Context mContext; @Mock private PackageManagerWrapper mPackageManager; @@ -109,10 +113,14 @@ public final class InstalledAppCounterTest { | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS | PackageManager.MATCH_ANY_USER, MAIN_USER_ID)).thenReturn(Arrays.asList( - buildInfo(MAIN_USER_ID, APP_1, ApplicationInfo.FLAG_UPDATED_SYSTEM_APP), - buildInfo(MAIN_USER_ID, APP_2, 0 /* flags */), - buildInfo(MAIN_USER_ID, APP_3, ApplicationInfo.FLAG_SYSTEM), - buildInfo(MAIN_USER_ID, APP_4, ApplicationInfo.FLAG_SYSTEM))); + buildInfo(MAIN_USER_APP_UID, APP_1, + ApplicationInfo.FLAG_UPDATED_SYSTEM_APP, 0 /* targetSdkVersion */), + buildInfo(MAIN_USER_APP_UID, APP_2, 0 /* flags */, + 0 /* targetSdkVersion */), + buildInfo(MAIN_USER_APP_UID, APP_3, ApplicationInfo.FLAG_SYSTEM, + 0 /* targetSdkVersion */), + buildInfo(MAIN_USER_APP_UID, APP_4, ApplicationInfo.FLAG_SYSTEM, + 0 /* targetSdkVersion */))); // For system apps, InstalledAppCounter checks whether they handle the default launcher // intent to decide whether to include them in the count of installed apps or not. expectQueryIntentActivities(MAIN_USER_ID, APP_3, true /* launchable */); @@ -129,14 +137,16 @@ public final class InstalledAppCounterTest { when(mPackageManager.getInstallReason(APP_4, mainUser)) .thenReturn(PackageManager.INSTALL_REASON_POLICY); - // The second user has four apps installed: + // The second user has two apps installed: // * app5 is a user-installed app. It should be counted. // * app6 is a system app that provides a launcher icon. It should be counted. when(mPackageManager.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, MANAGED_PROFILE_ID)).thenReturn(Arrays.asList( - buildInfo(MANAGED_PROFILE_ID, APP_5, 0 /* flags */), - buildInfo(MANAGED_PROFILE_ID, APP_6, ApplicationInfo.FLAG_SYSTEM))); + buildInfo(MANAGED_PROFILE_APP_UID, APP_5, 0 /* flags */, + 0 /* targetSdkVersion */), + buildInfo(MANAGED_PROFILE_APP_UID, APP_6, ApplicationInfo.FLAG_SYSTEM, + 0 /* targetSdkVersion */))); expectQueryIntentActivities(MANAGED_PROFILE_ID, APP_6, true /* launchable */); // app5 is installed by enterprise policy. @@ -165,7 +175,7 @@ public final class InstalledAppCounterTest { // Count once more, considering apps installed by enterprise policy only. Wait for the // background task to finish. - mInstalledAppCount = 0; + mInstalledAppCount = -1; (new InstalledAppCounterTestable(PackageManager.INSTALL_REASON_POLICY)).execute(); ShadowApplication.runBackgroundTasks(); diff --git a/tests/robotests/src/com/android/settings/enterprise/AdminGrantedCameraPermissionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/AdminGrantedCameraPermissionPreferenceControllerTest.java new file mode 100644 index 00000000000..de248856fb0 --- /dev/null +++ b/tests/robotests/src/com/android/settings/enterprise/AdminGrantedCameraPermissionPreferenceControllerTest.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2017 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.enterprise; + +import android.Manifest; + +import com.android.settings.R; +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +/** + * Tests for {@link AdminGrantedCameraPermissionPreferenceController}. + */ +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public final class AdminGrantedCameraPermissionPreferenceControllerTest extends + AdminGrantedPermissionsPreferenceControllerTestBase { + + public AdminGrantedCameraPermissionPreferenceControllerTest() { + super("enterprise_privacy_number_camera_access_packages", + new String[] {Manifest.permission.CAMERA}, + R.plurals.enterprise_privacy_number_camera_access_packages); + } + + @Override + public void setUp() { + super.setUp(); + mController = new AdminGrantedCameraPermissionPreferenceController(mContext); + } +} diff --git a/tests/robotests/src/com/android/settings/enterprise/AdminGrantedLocationPermissionsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/AdminGrantedLocationPermissionsPreferenceControllerTest.java new file mode 100644 index 00000000000..1c6f91d48ab --- /dev/null +++ b/tests/robotests/src/com/android/settings/enterprise/AdminGrantedLocationPermissionsPreferenceControllerTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2017 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.enterprise; + +import android.Manifest; + +import com.android.settings.R; +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +/** + * Tests for {@link AdminGrantedLocationPermissionsPreferenceController}. + */ +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public final class AdminGrantedLocationPermissionsPreferenceControllerTest extends + AdminGrantedPermissionsPreferenceControllerTestBase { + + public AdminGrantedLocationPermissionsPreferenceControllerTest() { + super("enterprise_privacy_number_location_access_packages", + new String[] {Manifest.permission.ACCESS_COARSE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION}, + R.plurals.enterprise_privacy_number_location_access_packages); + } + + @Override + public void setUp() { + super.setUp(); + mController = new AdminGrantedLocationPermissionsPreferenceController(mContext); + } +} diff --git a/tests/robotests/src/com/android/settings/enterprise/AdminGrantedMicrophonePermissionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/AdminGrantedMicrophonePermissionPreferenceControllerTest.java new file mode 100644 index 00000000000..bcaf63fd0da --- /dev/null +++ b/tests/robotests/src/com/android/settings/enterprise/AdminGrantedMicrophonePermissionPreferenceControllerTest.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2017 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.enterprise; + +import android.Manifest; + +import com.android.settings.R; +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +/** + * Tests for {@link AdminGrantedMicrophonePermissionPreferenceController}. + */ +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public final class AdminGrantedMicrophonePermissionPreferenceControllerTest extends + AdminGrantedPermissionsPreferenceControllerTestBase { + + public AdminGrantedMicrophonePermissionPreferenceControllerTest() { + super("enterprise_privacy_number_microphone_access_packages", + new String[] {Manifest.permission.RECORD_AUDIO}, + R.plurals.enterprise_privacy_number_microphone_access_packages); + } + + @Override + public void setUp() { + super.setUp(); + mController = new AdminGrantedMicrophonePermissionPreferenceController(mContext); + } +} diff --git a/tests/robotests/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerBaseTest.java b/tests/robotests/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerBaseTest.java new file mode 100644 index 00000000000..2bebbf07537 --- /dev/null +++ b/tests/robotests/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerBaseTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2017 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.enterprise; + +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +/** + * Tests for {@link AdminGrantedPermissionsPreferenceControllerBase}. + */ +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public final class AdminGrantedPermissionsPreferenceControllerBaseTest extends + AdminGrantedPermissionsPreferenceControllerTestBase { + + public AdminGrantedPermissionsPreferenceControllerBaseTest() { + super(null, new String[] {"some.permission"}, 123 /* resourceStringId */); + } + + @Override + public void setUp() { + super.setUp(); + mController = new AdminGrantedPermissionsPreferenceControllerBaseTestable(); + } + + private class AdminGrantedPermissionsPreferenceControllerBaseTestable extends + AdminGrantedPermissionsPreferenceControllerBase { + + AdminGrantedPermissionsPreferenceControllerBaseTestable() { + super(AdminGrantedPermissionsPreferenceControllerBaseTest.this.mContext, mPermissions, + mStringResourceId); + } + + @Override + public String getPreferenceKey() { + return null; + } + } +} diff --git a/tests/robotests/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerTestBase.java b/tests/robotests/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerTestBase.java new file mode 100644 index 00000000000..610692d12d0 --- /dev/null +++ b/tests/robotests/src/com/android/settings/enterprise/AdminGrantedPermissionsPreferenceControllerTestBase.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2017 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.enterprise; + +import android.content.Context; +import android.content.res.Resources; +import android.support.v7.preference.Preference; + +import com.android.settings.applications.ApplicationFeatureProvider; +import com.android.settings.testutils.FakeFeatureFactory; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.anyObject; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.when; + +/** + * Common base for testing subclasses of {@link AdminGrantedPermissionsPreferenceControllerBase}. + */ +public abstract class AdminGrantedPermissionsPreferenceControllerTestBase { + + protected final String mKey; + protected final String[] mPermissions; + protected final int mStringResourceId; + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + protected Context mContext; + private FakeFeatureFactory mFeatureFactory; + + protected AdminGrantedPermissionsPreferenceControllerBase mController; + + public AdminGrantedPermissionsPreferenceControllerTestBase(String key, String[] permissions, + int stringResourceId) { + mKey = key; + mPermissions = permissions; + mStringResourceId = stringResourceId; + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + FakeFeatureFactory.setupForTest(mContext); + mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext); + } + + private void setNumberOfPackagesWithAdminGrantedPermissions(int number) { + doAnswer(new Answer() { + public Object answer(InvocationOnMock invocation) { + ((ApplicationFeatureProvider.NumberOfAppsCallback) + invocation.getArguments()[1]).onNumberOfAppsResult(number); + return null; + }}).when(mFeatureFactory.applicationFeatureProvider) + .calculateNumberOfAppsWithAdminGrantedPermissions(eq(mPermissions), + anyObject()); + } + + @Test + public void testUpdateState() { + final Preference preference = new Preference(mContext, null, 0, 0); + preference.setVisible(true); + + setNumberOfPackagesWithAdminGrantedPermissions(20); + when(mContext.getResources().getQuantityString(mStringResourceId, 20, 20)) + .thenReturn("20 packages"); + mController.updateState(preference); + assertThat(preference.getTitle()).isEqualTo("20 packages"); + assertThat(preference.isVisible()).isTrue(); + + setNumberOfPackagesWithAdminGrantedPermissions(0); + mController.updateState(preference); + assertThat(preference.isVisible()).isFalse(); + } + + @Test + public void testIsAvailable() { + assertThat(mController.isAvailable()).isTrue(); + } + + @Test + public void testHandlePreferenceTreeClick() { + assertThat(mController.handlePreferenceTreeClick(new Preference(mContext, null, 0, 0))) + .isFalse(); + } + + @Test + public void testGetPreferenceKey() { + assertThat(mController.getPreferenceKey()).isEqualTo(mKey); + } +} diff --git a/tests/robotests/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceControllerTest.java index 9a46e1428c2..3dd1fd7c5db 100644 --- a/tests/robotests/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/enterprise/EnterpriseInstalledPackagesPreferenceControllerTest.java @@ -68,8 +68,8 @@ public final class EnterpriseInstalledPackagesPreferenceControllerTest { private void setNumberOfEnterpriseInstalledPackages(int number) { doAnswer(new Answer() { public Object answer(InvocationOnMock invocation) { - ((ApplicationFeatureProvider.NumberOfInstalledAppsCallback) - invocation.getArguments()[1]).onNumberOfInstalledAppsResult(number); + ((ApplicationFeatureProvider.NumberOfAppsCallback) + invocation.getArguments()[1]).onNumberOfAppsResult(number); return null; }}).when(mFeatureFactory.applicationFeatureProvider) .calculateNumberOfInstalledApps(eq(PackageManager.INSTALL_REASON_POLICY), diff --git a/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java b/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java index 5e4e08f4702..de4d02e3e9a 100644 --- a/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java +++ b/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java @@ -73,17 +73,23 @@ public final class EnterprisePrivacySettingsTest { final List controllers = mSettings.getPreferenceControllers( ShadowApplication.getInstance().getApplicationContext()); assertThat(controllers).isNotNull(); - assertThat(controllers.size()).isEqualTo(8); + assertThat(controllers.size()).isEqualTo(11); assertThat(controllers.get(0)).isInstanceOf(InstalledPackagesPreferenceController.class); assertThat(controllers.get(1)).isInstanceOf(NetworkLogsPreferenceController.class); assertThat(controllers.get(2)).isInstanceOf(BugReportsPreferenceController.class); assertThat(controllers.get(3)).isInstanceOf(SecurityLogsPreferenceController.class); assertThat(controllers.get(4)).isInstanceOf( - AlwaysOnVpnPrimaryUserPreferenceController.class); - assertThat(controllers.get(5)).isInstanceOf( - AlwaysOnVpnManagedProfilePreferenceController.class); - assertThat(controllers.get(6)).isInstanceOf(GlobalHttpProxyPreferenceController.class); - assertThat(controllers.get(7)).isInstanceOf( EnterpriseInstalledPackagesPreferenceController.class); + assertThat(controllers.get(5)).isInstanceOf( + AdminGrantedLocationPermissionsPreferenceController.class); + assertThat(controllers.get(6)).isInstanceOf( + AdminGrantedMicrophonePermissionPreferenceController.class); + assertThat(controllers.get(7)).isInstanceOf( + AdminGrantedCameraPermissionPreferenceController.class); + assertThat(controllers.get(8)).isInstanceOf( + AlwaysOnVpnPrimaryUserPreferenceController.class); + assertThat(controllers.get(9)).isInstanceOf( + AlwaysOnVpnManagedProfilePreferenceController.class); + assertThat(controllers.get(10)).isInstanceOf(GlobalHttpProxyPreferenceController.class); } } diff --git a/tests/robotests/src/com/android/settings/enterprise/InstalledPackagesPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/InstalledPackagesPreferenceControllerTest.java index 5a0da66288a..bf2c4ca1fcc 100644 --- a/tests/robotests/src/com/android/settings/enterprise/InstalledPackagesPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/enterprise/InstalledPackagesPreferenceControllerTest.java @@ -69,8 +69,8 @@ public final class InstalledPackagesPreferenceControllerTest { final Preference preference = new Preference(mContext, null, 0, 0); doAnswer(new Answer() { public Object answer(InvocationOnMock invocation) { - ((ApplicationFeatureProvider.NumberOfInstalledAppsCallback) - invocation.getArguments()[1]).onNumberOfInstalledAppsResult(20); + ((ApplicationFeatureProvider.NumberOfAppsCallback) + invocation.getArguments()[1]).onNumberOfAppsResult(20); return null; }}).when(mFeatureFactory.applicationFeatureProvider) .calculateNumberOfInstalledApps( diff --git a/tests/robotests/src/com/android/settings/search2/InstalledAppResultLoaderTest.java b/tests/robotests/src/com/android/settings/search2/InstalledAppResultLoaderTest.java index 24aa94b70a7..8fde73d88db 100644 --- a/tests/robotests/src/com/android/settings/search2/InstalledAppResultLoaderTest.java +++ b/tests/robotests/src/com/android/settings/search2/InstalledAppResultLoaderTest.java @@ -64,11 +64,16 @@ public class InstalledAppResultLoaderTest { when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt())) .thenReturn(Arrays.asList( - ApplicationTestUtils.buildInfo(0 /* uid */, "app1", FLAG_SYSTEM), - ApplicationTestUtils.buildInfo(0 /* uid */, "app2", FLAG_SYSTEM), - ApplicationTestUtils.buildInfo(0 /* uid */, "app3", FLAG_SYSTEM), - ApplicationTestUtils.buildInfo(0 /* uid */, "app4", 0 /* flags */), - ApplicationTestUtils.buildInfo(0 /* uid */, "app", 0 /* flags */))); + ApplicationTestUtils.buildInfo(0 /* uid */, "app1", FLAG_SYSTEM, + 0 /* targetSdkVersion */), + ApplicationTestUtils.buildInfo(0 /* uid */, "app2", FLAG_SYSTEM, + 0 /* targetSdkVersion */), + ApplicationTestUtils.buildInfo(0 /* uid */, "app3", FLAG_SYSTEM, + 0 /* targetSdkVersion */), + ApplicationTestUtils.buildInfo(0 /* uid */, "app4", 0 /* flags */, + 0 /* targetSdkVersion */), + ApplicationTestUtils.buildInfo(0 /* uid */, "app", 0 /* flags */, + 0 /* targetSdkVersion */))); } @Test diff --git a/tests/robotests/src/com/android/settings/testutils/ApplicationTestUtils.java b/tests/robotests/src/com/android/settings/testutils/ApplicationTestUtils.java index 87899282216..352d128f34d 100644 --- a/tests/robotests/src/com/android/settings/testutils/ApplicationTestUtils.java +++ b/tests/robotests/src/com/android/settings/testutils/ApplicationTestUtils.java @@ -15,7 +15,6 @@ package com.android.settings.testutils; import android.content.pm.ApplicationInfo; -import android.os.UserHandle; /** * Helper for mocking installed applications. @@ -25,19 +24,21 @@ public class ApplicationTestUtils { * Create and populate an {@link android.content.pm.ApplicationInfo} object that describes an * installed app. * - * @param userId The user id that this app is installed for. Typical values are 0 for the - * system user and 10, 11, 12... for secondary users. + * @param uid The app's uid * @param packageName The app's package name. * @param flags Flags describing the app. See {@link android.content.pm.ApplicationInfo#flags} * for possible values. + * @param targetSdkVersion The app's target SDK version * * @see android.content.pm.ApplicationInfo */ - public static ApplicationInfo buildInfo(int userId, String packageName, int flags) { + public static ApplicationInfo buildInfo(int uid, String packageName, int flags, + int targetSdkVersion) { final ApplicationInfo info = new ApplicationInfo(); - info.uid = UserHandle.getUid(userId, 1); + info.uid = uid; info.packageName = packageName; info.flags = flags; + info.targetSdkVersion = targetSdkVersion; return info; } }