Merge "Settings for new accessibility shortcut."
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4ac217f..54ed1df 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -8006,6 +8006,26 @@
<string name="enterprise_privacy_security_logs">Your most recent security log</string>
<!-- Label indicating that the date at which the admin last took a particular action was "never" (i.e. the admin never took the action so far). -->
<string name="enterprise_privacy_never">Never</string>
+ <!-- Label indicating how many apps were installed on the device by the admin. [CHAR LIMIT=NONE] -->
+ <plurals name="enterprise_privacy_number_enterprise_installed_packages">
+ <item quantity="one"><xliff:g id="count">%d</xliff:g> app installed by your admin</item>
+ <item quantity="other"><xliff:g id="count">%d</xliff:g> apps installed by your admin</item>
+ </plurals>
+ <!-- Label indicating how many apps were granted permission to access the device's location by the admin. [CHAR LIMIT=NONE] -->
+ <plurals name="enterprise_privacy_number_location_access_packages">
+ <item quantity="one"><xliff:g id="count">%d</xliff:g> app allowed access to your location by your admin</item>
+ <item quantity="other"><xliff:g id="count">%d</xliff:g> apps allowed access to your location by your admin</item>
+ </plurals>
+ <!-- Label indicating how many apps were granted permission to access the microphone by the admin. [CHAR LIMIT=NONE] -->
+ <plurals name="enterprise_privacy_number_microphone_access_packages">
+ <item quantity="one"><xliff:g id="count">%d</xliff:g> app allowed access to your microphone by your admin</item>
+ <item quantity="other"><xliff:g id="count">%d</xliff:g> apps allowed access to your microphone by your admin</item>
+ </plurals>
+ <!-- Label indicating how many apps were granted permission to access the camera by the admin. [CHAR LIMIT=NONE] -->
+ <plurals name="enterprise_privacy_number_camera_access_packages">
+ <item quantity="one"><xliff:g id="count">%d</xliff:g> app allowed access to your camera by your admin</item>
+ <item quantity="other"><xliff:g id="count">%d</xliff:g> apps allowed access to your camera by your admin</item>
+ </plurals>
<!-- Label explaining that an always-on VPN was set by the admin for the entire device. [CHAR LIMIT=NONE] -->
<string name="enterprise_privacy_always_on_vpn_device">Always-on VPN turned on</string>
<!-- Label explaining that an always-on VPN was set by the admin in the personal profile. [CHAR LIMIT=NONE] -->
@@ -8014,11 +8034,6 @@
<string name="enterprise_privacy_always_on_vpn_work">Always-on VPN turned on in your work profile</string>
<!-- Label explaining that a global HTTP proxy was set by the admin. [CHAR LIMIT=NONE] -->
<string name="enterprise_privacy_global_http_proxy">Global HTTP proxy set</string>
- <!-- Label indicating how many apps were installed on the device by the admin. [CHAR LIMIT=NONE] -->
- <plurals name="enterprise_privacy_number_enterprise_installed_packages">
- <item quantity="one"><xliff:g id="count">%d</xliff:g> app installed by your admin</item>
- <item quantity="other"><xliff:g id="count">%d</xliff:g> apps installed by your admin</item>
- </plurals>
<!-- Preference label for the Photos & Videos storage section. [CHAR LIMIT=50] -->
<string name="storage_photos_videos">Photos & Videos</string>
diff --git a/res/xml/enterprise_privacy_settings.xml b/res/xml/enterprise_privacy_settings.xml
index 9fed938..4bb2a8c 100644
--- a/res/xml/enterprise_privacy_settings.xml
+++ b/res/xml/enterprise_privacy_settings.xml
@@ -60,6 +60,22 @@
<PreferenceCategory android:title="@string/enterprise_privacy_exposure_changes_category">
<com.android.settings.DividerPreference
+ android:key="number_enterprise_installed_packages"
+ settings:allowDividerBelow="true"
+ settings:multiLine="true"/>
+ <com.android.settings.DividerPreference
+ android:key="enterprise_privacy_number_location_access_packages"
+ settings:allowDividerBelow="true"
+ settings:multiLine="true"/>
+ <com.android.settings.DividerPreference
+ android:key="enterprise_privacy_number_microphone_access_packages"
+ settings:allowDividerBelow="true"
+ settings:multiLine="true"/>
+ <com.android.settings.DividerPreference
+ android:key="enterprise_privacy_number_camera_access_packages"
+ settings:allowDividerBelow="true"
+ settings:multiLine="true"/>
+ <com.android.settings.DividerPreference
android:key="always_on_vpn_primary_user"
settings:allowDividerBelow="true"
settings:multiLine="true"/>
@@ -73,10 +89,6 @@
android:title="@string/enterprise_privacy_global_http_proxy"
settings:allowDividerBelow="true"
settings:multiLine="true"/>
- <com.android.settings.DividerPreference
- android:key="number_enterprise_installed_packages"
- settings:allowDividerBelow="true"
- settings:multiLine="true"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/enterprise_privacy_device_access_category">
diff --git a/src/com/android/settings/DevelopmentSettings.java b/src/com/android/settings/DevelopmentSettings.java
index 27ff937..8184905 100644
--- a/src/com/android/settings/DevelopmentSettings.java
+++ b/src/com/android/settings/DevelopmentSettings.java
@@ -1779,49 +1779,50 @@
private void initBluetoothConfigurationValues() {
String[] values;
- String[] titles;
+ String[] summaries;
int index;
// Init the Codec Type - Default
values = getResources().getStringArray(R.array.bluetooth_a2dp_codec_values);
- titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_titles);
+ summaries = getResources().getStringArray(R.array.bluetooth_a2dp_codec_summaries);
index = 0;
mBluetoothSelectA2dpCodec.setValue(values[index]);
- mBluetoothSelectA2dpCodec.setSummary(titles[index]);
+ mBluetoothSelectA2dpCodec.setSummary(summaries[index]);
// Init the Sample Rate - Default
values = getResources().getStringArray(R.array.bluetooth_a2dp_codec_sample_rate_values);
- titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_sample_rate_titles);
+ summaries = getResources().getStringArray(R.array.bluetooth_a2dp_codec_sample_rate_summaries);
index = 0;
mBluetoothSelectA2dpSampleRate.setValue(values[index]);
- mBluetoothSelectA2dpSampleRate.setSummary(titles[index]);
+ mBluetoothSelectA2dpSampleRate.setSummary(summaries[index]);
// Init the Bits Per Sample - Default
values = getResources().getStringArray(R.array.bluetooth_a2dp_codec_bits_per_sample_values);
- titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_bits_per_sample_titles);
+ summaries = getResources().getStringArray(R.array.bluetooth_a2dp_codec_bits_per_sample_summaries);
index = 0;
mBluetoothSelectA2dpBitsPerSample.setValue(values[index]);
- mBluetoothSelectA2dpBitsPerSample.setSummary(titles[index]);
+ mBluetoothSelectA2dpBitsPerSample.setSummary(summaries[index]);
// Init the Channel Mode - Default
values = getResources().getStringArray(R.array.bluetooth_a2dp_codec_channel_mode_values);
- titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_channel_mode_titles);
+ summaries = getResources().getStringArray(R.array.bluetooth_a2dp_codec_channel_mode_summaries);
index = 0;
mBluetoothSelectA2dpChannelMode.setValue(values[index]);
- mBluetoothSelectA2dpChannelMode.setSummary(titles[index]);
+ mBluetoothSelectA2dpChannelMode.setSummary(summaries[index]);
// Init the LDAC Playback Quality - High
values = getResources().getStringArray(R.array.bluetooth_a2dp_codec_ldac_playback_quality_values);
- titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_ldac_playback_quality_titles);
+ summaries = getResources().getStringArray(R.array.bluetooth_a2dp_codec_ldac_playback_quality_summaries);
index = 0;
mBluetoothSelectA2dpLdacPlaybackQuality.setValue(values[index]);
- mBluetoothSelectA2dpLdacPlaybackQuality.setSummary(titles[index]);
+ mBluetoothSelectA2dpLdacPlaybackQuality.setSummary(summaries[index]);
}
private void updateBluetoothA2dpConfigurationValues() {
int index;
- String[] titles;
+ String[] summaries;
BluetoothCodecConfig codecConfig = null;
+ String streaming;
synchronized (mBluetoothA2dpLock) {
if (mBluetoothA2dp != null) {
@@ -1851,8 +1852,9 @@
break;
}
if (index >= 0) {
- titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_titles);
- mBluetoothSelectA2dpCodec.setSummary("Streaming: " + titles[index]);
+ summaries = getResources().getStringArray(R.array.bluetooth_a2dp_codec_summaries);
+ streaming = getResources().getString(R.string.bluetooth_select_a2dp_codec_streaming_label, summaries[index]);
+ mBluetoothSelectA2dpCodec.setSummary(streaming);
}
// Update the Sample Rate
@@ -1877,8 +1879,9 @@
break;
}
if (index >= 0) {
- titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_sample_rate_titles);
- mBluetoothSelectA2dpSampleRate.setSummary("Streaming: " + titles[index]);
+ summaries = getResources().getStringArray(R.array.bluetooth_a2dp_codec_sample_rate_summaries);
+ streaming = getResources().getString(R.string.bluetooth_select_a2dp_codec_streaming_label, summaries[index]);
+ mBluetoothSelectA2dpSampleRate.setSummary(streaming);
}
// Update the Bits Per Sample
@@ -1898,8 +1901,9 @@
break;
}
if (index >= 0) {
- titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_bits_per_sample_titles);
- mBluetoothSelectA2dpBitsPerSample.setSummary("Streaming: " + titles[index]);
+ summaries = getResources().getStringArray(R.array.bluetooth_a2dp_codec_bits_per_sample_summaries);
+ streaming = getResources().getString(R.string.bluetooth_select_a2dp_codec_streaming_label, summaries[index]);
+ mBluetoothSelectA2dpBitsPerSample.setSummary(streaming);
}
// Update the Channel Mode
@@ -1916,34 +1920,41 @@
break;
}
if (index >= 0) {
- titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_channel_mode_titles);
- mBluetoothSelectA2dpChannelMode.setSummary("Streaming: " + titles[index]);
+ summaries = getResources().getStringArray(R.array.bluetooth_a2dp_codec_channel_mode_summaries);
+ streaming = getResources().getString(R.string.bluetooth_select_a2dp_codec_streaming_label, summaries[index]);
+ mBluetoothSelectA2dpChannelMode.setSummary(streaming);
}
// Update the LDAC Playback Quality
- index = -1;
- switch ((int)codecConfig.getCodecSpecific1()) {
- case 1000:
- index = 0;
- break;
- case 1001:
- index = 1;
- break;
- case 1002:
- index = 2;
+ // The actual values are 0, 1, 2 - those are extracted
+ // as mod-10 remainders of a larger value.
+ // The reason is because within BluetoothCodecConfig we cannot use
+ // a codec-specific value of zero.
+ index = (int)codecConfig.getCodecSpecific1();
+ if (index > 0) {
+ index %= 10;
+ } else {
+ index = -1;
+ }
+ switch (index) {
+ case 0:
+ case 1:
+ case 2:
break;
default:
+ index = -1;
break;
}
if (index >= 0) {
- titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_ldac_playback_quality_titles);
- mBluetoothSelectA2dpLdacPlaybackQuality.setSummary("Streaming: " + titles[index]);
+ summaries = getResources().getStringArray(R.array.bluetooth_a2dp_codec_ldac_playback_quality_summaries);
+ streaming = getResources().getString(R.string.bluetooth_select_a2dp_codec_streaming_label, summaries[index]);
+ mBluetoothSelectA2dpLdacPlaybackQuality.setSummary(streaming);
}
}
private void writeBluetoothConfigurationOption(Preference preference,
Object newValue) {
- String[] titles;
+ String[] summaries;
int index;
int codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID;
int codecPriorityValue = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
@@ -1961,8 +1972,8 @@
codecType = newValue.toString();
index = mBluetoothSelectA2dpCodec.findIndexOfValue(newValue.toString());
if (index >= 0) {
- titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_titles);
- mBluetoothSelectA2dpCodec.setSummary(titles[index]);
+ summaries = getResources().getStringArray(R.array.bluetooth_a2dp_codec_summaries);
+ mBluetoothSelectA2dpCodec.setSummary(summaries[index]);
}
}
index = mBluetoothSelectA2dpCodec.findIndexOfValue(codecType);
@@ -2015,8 +2026,8 @@
sampleRate = newValue.toString();
index = mBluetoothSelectA2dpSampleRate.findIndexOfValue(newValue.toString());
if (index >= 0) {
- titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_sample_rate_titles);
- mBluetoothSelectA2dpSampleRate.setSummary(titles[index]);
+ summaries = getResources().getStringArray(R.array.bluetooth_a2dp_codec_sample_rate_summaries);
+ mBluetoothSelectA2dpSampleRate.setSummary(summaries[index]);
}
}
index = mBluetoothSelectA2dpSampleRate.findIndexOfValue(sampleRate);
@@ -2046,8 +2057,8 @@
bitsPerSample = newValue.toString();
index = mBluetoothSelectA2dpBitsPerSample.findIndexOfValue(newValue.toString());
if (index >= 0) {
- titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_bits_per_sample_titles);
- mBluetoothSelectA2dpBitsPerSample.setSummary(titles[index]);
+ summaries = getResources().getStringArray(R.array.bluetooth_a2dp_codec_bits_per_sample_summaries);
+ mBluetoothSelectA2dpBitsPerSample.setSummary(summaries[index]);
}
}
index = mBluetoothSelectA2dpBitsPerSample.findIndexOfValue(bitsPerSample);
@@ -2074,8 +2085,8 @@
channelMode = newValue.toString();
index = mBluetoothSelectA2dpChannelMode.findIndexOfValue(newValue.toString());
if (index >= 0) {
- titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_channel_mode_titles);
- mBluetoothSelectA2dpChannelMode.setSummary(titles[index]);
+ summaries = getResources().getStringArray(R.array.bluetooth_a2dp_codec_channel_mode_summaries);
+ mBluetoothSelectA2dpChannelMode.setSummary(summaries[index]);
}
}
index = mBluetoothSelectA2dpChannelMode.findIndexOfValue(channelMode);
@@ -2099,20 +2110,16 @@
ldacPlaybackQuality = newValue.toString();
index = mBluetoothSelectA2dpLdacPlaybackQuality.findIndexOfValue(newValue.toString());
if (index >= 0) {
- titles = getResources().getStringArray(R.array.bluetooth_a2dp_codec_ldac_playback_quality_titles);
- mBluetoothSelectA2dpLdacPlaybackQuality.setSummary(titles[index]);
+ summaries = getResources().getStringArray(R.array.bluetooth_a2dp_codec_ldac_playback_quality_summaries);
+ mBluetoothSelectA2dpLdacPlaybackQuality.setSummary(summaries[index]);
}
}
index = mBluetoothSelectA2dpLdacPlaybackQuality.findIndexOfValue(ldacPlaybackQuality);
switch (index) {
case 0:
- codecSpecific1Value = 1000;
- break;
case 1:
- codecSpecific1Value = 1001;
- break;
case 2:
- codecSpecific1Value = 1002;
+ codecSpecific1Value = 1000 + index;
break;
default:
break;
diff --git a/src/com/android/settings/DeviceAdminAdd.java b/src/com/android/settings/DeviceAdminAdd.java
index 14d42c0..d6a072c 100644
--- a/src/com/android/settings/DeviceAdminAdd.java
+++ b/src/com/android/settings/DeviceAdminAdd.java
@@ -58,6 +58,9 @@
import android.widget.ImageView;
import android.widget.TextView;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
+import com.android.settings.overlay.FeatureFactory;
import com.android.settings.users.UserDialogs;
import org.xmlpull.v1.XmlPullParserException;
@@ -395,6 +398,7 @@
void addAndFinish() {
try {
+ logSpecialPermissionChange(true, mDeviceAdmin.getComponent().getPackageName());
mDPM.setActiveAdmin(mDeviceAdmin.getComponent(), mRefreshing);
EventLog.writeEvent(EventLogTags.EXP_DET_DEVICE_ADMIN_ACTIVATED_BY_USER,
mDeviceAdmin.getActivityInfo().applicationInfo.uid);
@@ -429,6 +433,7 @@
ActivityManager.getService().resumeAppSwitches();
} catch (RemoteException e) {
}
+ logSpecialPermissionChange(false, mDeviceAdmin.getComponent().getPackageName());
mDPM.removeActiveAdmin(mDeviceAdmin.getComponent());
finish();
} else {
@@ -444,6 +449,12 @@
}
}
+ void logSpecialPermissionChange(boolean allow, String packageName) {
+ int logCategory = allow ? MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_ADMIN_ALLOW :
+ MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_ADMIN_DENY;
+ FeatureFactory.getFactory(this).getMetricsFeatureProvider().action(this, logCategory, packageName);
+ }
+
@Override
protected void onResume() {
super.onResume();
diff --git a/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounter.java b/src/com/android/settings/applications/AppWithAdminGrantedPermissionsCounter.java
new file mode 100644
index 0000000..331d384
--- /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 1db33a6..101ae91 100644
--- a/src/com/android/settings/applications/ApplicationFeatureProvider.java
+++ b/src/com/android/settings/applications/ApplicationFeatureProvider.java
@@ -35,17 +35,29 @@
* 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 902008c..ff7f0f0 100644
--- a/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java
+++ b/src/com/android/settings/applications/ApplicationFeatureProviderImpl.java
@@ -18,21 +18,29 @@
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 @@
}
@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<UserInfo> 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/applications/DrawOverlayDetails.java b/src/com/android/settings/applications/DrawOverlayDetails.java
index dfaa95f..39b8919 100644
--- a/src/com/android/settings/applications/DrawOverlayDetails.java
+++ b/src/com/android/settings/applications/DrawOverlayDetails.java
@@ -31,10 +31,12 @@
import android.support.v7.preference.Preference.OnPreferenceClickListener;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.applications.AppStateAppOpsBridge.PermissionState;
import com.android.settings.applications.AppStateOverlayBridge.OverlayState;
+import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
public class DrawOverlayDetails extends AppInfoWithHeader implements OnPreferenceChangeListener,
@@ -121,11 +123,20 @@
}
private void setCanDrawOverlay(boolean newState) {
+ logSpecialPermissionChange(newState, mPackageName);
mAppOpsManager.setMode(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
mPackageInfo.applicationInfo.uid, mPackageName, newState
? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_ERRORED);
}
+ @VisibleForTesting
+ void logSpecialPermissionChange(boolean newState, String packageName) {
+ int logCategory = newState ? MetricsEvent.APP_SPECIAL_PERMISSION_APPDRAW_ALLOW
+ : MetricsEvent.APP_SPECIAL_PERMISSION_APPDRAW_DENY;
+ FeatureFactory.getFactory(getContext())
+ .getMetricsFeatureProvider().action(getContext(), logCategory, packageName);
+ }
+
private boolean canDrawOverlay(String pkgName) {
int result = mAppOpsManager.noteOpNoThrow(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
mPackageInfo.applicationInfo.uid, pkgName);
diff --git a/src/com/android/settings/applications/PremiumSmsAccess.java b/src/com/android/settings/applications/PremiumSmsAccess.java
index fa97537..2b0942f 100644
--- a/src/com/android/settings/applications/PremiumSmsAccess.java
+++ b/src/com/android/settings/applications/PremiumSmsAccess.java
@@ -24,13 +24,17 @@
import android.support.v7.preference.PreferenceScreen;
import android.support.v7.preference.PreferenceViewHolder;
import android.view.View;
+
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.telephony.SmsUsageMonitor;
import com.android.settings.DividerPreference;
import com.android.settings.R;
import com.android.settings.applications.AppStateBaseBridge.Callback;
import com.android.settings.applications.AppStateSmsPremBridge.SmsState;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
import com.android.settings.notification.EmptyTextSettings;
+import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.applications.ApplicationsState.Callbacks;
@@ -81,11 +85,33 @@
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
PremiumSmsPreference pref = (PremiumSmsPreference) preference;
- mSmsBackend.setSmsState(pref.mAppEntry.info.packageName,
- Integer.parseInt((String) newValue));
+ int smsState = Integer.parseInt((String) newValue);
+ logSpecialPermissionChange(smsState, pref.mAppEntry.info.packageName);
+ mSmsBackend.setSmsState(pref.mAppEntry.info.packageName, smsState);
return true;
}
+ @VisibleForTesting
+ void logSpecialPermissionChange(int smsState, String packageName) {
+ int category = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN;
+ switch (smsState) {
+ case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER:
+ category = MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_PREMIUM_SMS_ASK;
+ break;
+ case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW:
+ category = MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_PREMIUM_SMS_DENY;
+ break;
+ case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW:
+ category = MetricsProto.MetricsEvent.
+ APP_SPECIAL_PERMISSION_PREMIUM_SMS_ALWAYS_ALLOW;
+ break;
+ }
+ if (category != SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) {
+ FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider().action(
+ getContext(), category, packageName);
+ }
+ }
+
private void updatePrefs(ArrayList<AppEntry> apps) {
if (apps == null) return;
setEmptyText(R.string.premium_sms_none);
diff --git a/src/com/android/settings/applications/UsageAccessDetails.java b/src/com/android/settings/applications/UsageAccessDetails.java
index 2fa0253..e40ae37 100644
--- a/src/com/android/settings/applications/UsageAccessDetails.java
+++ b/src/com/android/settings/applications/UsageAccessDetails.java
@@ -33,9 +33,12 @@
import android.support.v7.preference.Preference.OnPreferenceClickListener;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.applications.AppStateUsageBridge.UsageState;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
+import com.android.settings.overlay.FeatureFactory;
public class UsageAccessDetails extends AppInfoWithHeader implements OnPreferenceChangeListener,
OnPreferenceClickListener {
@@ -119,10 +122,19 @@
}
private void setHasAccess(boolean newState) {
+ logSpecialPermissionChange(newState, mPackageName);
mAppOpsManager.setMode(AppOpsManager.OP_GET_USAGE_STATS, mPackageInfo.applicationInfo.uid,
mPackageName, newState ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
}
+ @VisibleForTesting
+ void logSpecialPermissionChange(boolean newState, String packageName) {
+ int logCategory = newState ? MetricsEvent.APP_SPECIAL_PERMISSION_USAGE_VIEW_ALLOW
+ : MetricsEvent.APP_SPECIAL_PERMISSION_USAGE_VIEW_DENY;
+ FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider().action(getContext(),
+ logCategory, packageName);
+ }
+
@Override
protected boolean refreshUi() {
mUsageState = mUsageBridge.getUsageInfo(mPackageName,
diff --git a/src/com/android/settings/applications/VrListenerSettings.java b/src/com/android/settings/applications/VrListenerSettings.java
index 08d1367..99340b1 100644
--- a/src/com/android/settings/applications/VrListenerSettings.java
+++ b/src/com/android/settings/applications/VrListenerSettings.java
@@ -15,11 +15,14 @@
*/
package com.android.settings.applications;
+import android.content.ComponentName;
import android.provider.Settings;
import android.service.vr.VrListenerService;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
+import com.android.settings.overlay.FeatureFactory;
import com.android.settings.utils.ManagedServiceSettings;
public class VrListenerSettings extends ManagedServiceSettings {
@@ -48,4 +51,18 @@
public int getMetricsCategory() {
return MetricsEvent.VR_MANAGE_LISTENERS;
}
+
+ @Override
+ protected boolean setEnabled(ComponentName service, String title, boolean enable) {
+ logSpecialPermissionChange(enable, service.getPackageName());
+ return super.setEnabled(service, title, enable);
+ }
+
+ @VisibleForTesting
+ void logSpecialPermissionChange(boolean enable, String packageName) {
+ int logCategory = enable ? MetricsEvent.APP_SPECIAL_PERMISSION_VRHELPER_ALLOW
+ : MetricsEvent.APP_SPECIAL_PERMISSION_VRHELPER_DENY;
+ FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider().action(getContext(),
+ logCategory, packageName);
+ }
}
diff --git a/src/com/android/settings/applications/WriteSettingsDetails.java b/src/com/android/settings/applications/WriteSettingsDetails.java
index 9f9016d..aea05b3 100644
--- a/src/com/android/settings/applications/WriteSettingsDetails.java
+++ b/src/com/android/settings/applications/WriteSettingsDetails.java
@@ -35,6 +35,7 @@
import com.android.settings.R;
import com.android.settings.applications.AppStateAppOpsBridge.PermissionState;
import com.android.settings.applications.AppStateWriteSettingsBridge.WriteSettingsState;
+import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import java.util.List;
@@ -117,11 +118,19 @@
}
private void setCanWriteSettings(boolean newState) {
+ logSpecialPermissionChange(newState, mPackageName);
mAppOpsManager.setMode(AppOpsManager.OP_WRITE_SETTINGS,
mPackageInfo.applicationInfo.uid, mPackageName, newState
? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_ERRORED);
}
+ void logSpecialPermissionChange(boolean newState, String packageName) {
+ int logCategory = newState ? MetricsEvent.APP_SPECIAL_PERMISSION_SETTINGS_CHANGE_ALLOW
+ : MetricsEvent.APP_SPECIAL_PERMISSION_SETTINGS_CHANGE_DENY;
+ FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider().action(getContext(),
+ logCategory, packageName);
+ }
+
private boolean canWriteSettings(String pkgName) {
int result = mAppOpsManager.noteOpNoThrow(AppOpsManager.OP_WRITE_SETTINGS,
mPackageInfo.applicationInfo.uid, pkgName);
diff --git a/src/com/android/settings/datausage/UnrestrictedDataAccess.java b/src/com/android/settings/datausage/UnrestrictedDataAccess.java
index 0839114..58a34b9 100644
--- a/src/com/android/settings/datausage/UnrestrictedDataAccess.java
+++ b/src/com/android/settings/datausage/UnrestrictedDataAccess.java
@@ -18,7 +18,6 @@
import android.content.Context;
import android.os.Bundle;
import android.os.UserHandle;
-import android.support.annotation.VisibleForTesting;
import android.support.v14.preference.SwitchPreference;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceViewHolder;
@@ -27,12 +26,14 @@
import android.view.MenuItem;
import android.view.View;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.applications.AppStateBaseBridge;
import com.android.settings.applications.InstalledAppDetails;
import com.android.settings.datausage.AppStateDataUsageBridge.DataUsageState;
+import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.applications.ApplicationsState.AppFilter;
@@ -215,6 +216,7 @@
if (preference instanceof AccessPreference) {
AccessPreference accessPreference = (AccessPreference) preference;
boolean whitelisted = newValue == Boolean.TRUE;
+ logSpecialPermissionChange(whitelisted, accessPreference.mEntry.info.packageName);
mDataSaverBackend.setIsWhitelisted(accessPreference.mEntry.info.uid,
accessPreference.mEntry.info.packageName, whitelisted);
accessPreference.mState.isDataSaverWhitelisted = whitelisted;
@@ -224,6 +226,14 @@
}
@VisibleForTesting
+ void logSpecialPermissionChange(boolean whitelisted, String packageName) {
+ int logCategory = whitelisted ? MetricsEvent.APP_SPECIAL_PERMISSION_UNL_DATA_ALLOW
+ : MetricsEvent.APP_SPECIAL_PERMISSION_UNL_DATA_DENY;
+ FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider().action(getContext(),
+ logCategory, packageName);
+ }
+
+ @VisibleForTesting
boolean shouldAddPreference(AppEntry app) {
return app != null && UserHandle.isApp(app.info.uid);
}
diff --git a/src/com/android/settings/enterprise/AdminActionPreferenceControllerBase.java b/src/com/android/settings/enterprise/AdminActionPreferenceControllerBase.java
index 99ff62e..ef03cfb 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 0000000..a2137ff
--- /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 0000000..0453b53
--- /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 0000000..3adde92
--- /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 0000000..2ca5451
--- /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 d65292f..a23448e 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
@@ -33,6 +34,14 @@
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()}.
*
* @see android.app.admin.DevicePolicyManager#getLastSecurityLogRetrievalTime
diff --git a/src/com/android/settings/enterprise/DevicePolicyManagerWrapperImpl.java b/src/com/android/settings/enterprise/DevicePolicyManagerWrapperImpl.java
index 710e5ac..d122ec6 100644
--- a/src/com/android/settings/enterprise/DevicePolicyManagerWrapperImpl.java
+++ b/src/com/android/settings/enterprise/DevicePolicyManagerWrapperImpl.java
@@ -18,6 +18,7 @@
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
+import android.support.annotation.Nullable;
public class DevicePolicyManagerWrapperImpl implements DevicePolicyManagerWrapper {
private final DevicePolicyManager mDpm;
@@ -32,6 +33,12 @@
}
@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 2cd2862..208bf0c 100644
--- a/src/com/android/settings/enterprise/EnterprisePrivacySettings.java
+++ b/src/com/android/settings/enterprise/EnterprisePrivacySettings.java
@@ -19,8 +19,8 @@
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 @@
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/fingerprint/SetupFingerprintEnrollIntroduction.java b/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroduction.java
index 546fc0e..3951aff 100644
--- a/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroduction.java
+++ b/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroduction.java
@@ -16,6 +16,7 @@
package com.android.settings.fingerprint;
+import android.app.KeyguardManager;
import android.content.Intent;
import android.content.res.Resources;
import android.os.UserHandle;
@@ -83,9 +84,17 @@
@Override
protected void onCancelButtonClick() {
- SetupSkipDialog dialog = SetupSkipDialog.newInstance(
- getIntent().getBooleanExtra(SetupSkipDialog.EXTRA_FRP_SUPPORTED, false));
- dialog.show(getFragmentManager());
+ KeyguardManager keyguardManager = getSystemService(KeyguardManager.class);
+ if (keyguardManager.isKeyguardSecure()) {
+ // If the keyguard is already set up securely (maybe the user added a backup screen
+ // lock and skipped fingerprint), return RESULT_SKIP directly.
+ setResult(RESULT_SKIP);
+ finish();
+ } else {
+ SetupSkipDialog dialog = SetupSkipDialog.newInstance(
+ getIntent().getBooleanExtra(SetupSkipDialog.EXTRA_FRP_SUPPORTED, false));
+ dialog.show(getFragmentManager());
+ }
}
@Override
diff --git a/src/com/android/settings/fuelgauge/HighPowerDetail.java b/src/com/android/settings/fuelgauge/HighPowerDetail.java
index a59dc78..296f973 100644
--- a/src/com/android/settings/fuelgauge/HighPowerDetail.java
+++ b/src/com/android/settings/fuelgauge/HighPowerDetail.java
@@ -30,10 +30,12 @@
import android.widget.Checkable;
import android.widget.TextView;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.applications.AppInfoBase;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
public class HighPowerDetail extends InstrumentedDialogFragment implements OnClickListener,
@@ -125,6 +127,7 @@
boolean newValue = mIsEnabled;
boolean oldValue = mBackend.isWhitelisted(mPackageName);
if (newValue != oldValue) {
+ logSpecialPermissionChange(newValue, mPackageName, getContext());
if (newValue) {
mBackend.addApp(mPackageName);
} else {
@@ -134,6 +137,14 @@
}
}
+ @VisibleForTesting
+ static void logSpecialPermissionChange(boolean whitelist, String packageName, Context context) {
+ int logCategory = whitelist ? MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_BATTERY_DENY
+ : MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_BATTERY_ALLOW;
+ FeatureFactory.getFactory(context).getMetricsFeatureProvider().action(context, logCategory,
+ packageName);
+ }
+
@Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
diff --git a/src/com/android/settings/notification/NotificationAccessSettings.java b/src/com/android/settings/notification/NotificationAccessSettings.java
index b032358..2cd728c 100644
--- a/src/com/android/settings/notification/NotificationAccessSettings.java
+++ b/src/com/android/settings/notification/NotificationAccessSettings.java
@@ -29,9 +29,12 @@
import android.service.notification.NotificationListenerService;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.overlay.FeatureFactory;
import com.android.settings.utils.ManagedServiceSettings;
public class NotificationAccessSettings extends ManagedServiceSettings {
@@ -68,6 +71,7 @@
}
protected boolean setEnabled(ComponentName service, String title, boolean enable) {
+ logSpecialPermissionChange(enable, service.getPackageName());
if (!enable) {
if (!mServiceListing.isEnabled(service)) {
return true; // already disabled
@@ -82,6 +86,14 @@
}
}
+ @VisibleForTesting
+ void logSpecialPermissionChange(boolean enable, String packageName) {
+ int logCategory = enable ? MetricsEvent.APP_SPECIAL_PERMISSION_NOTIVIEW_ALLOW
+ : MetricsEvent.APP_SPECIAL_PERMISSION_NOTIVIEW_DENY;
+ FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider().action(getContext(),
+ logCategory, packageName);
+ }
+
private static void disable(final Context context, final NotificationAccessSettings parent,
final ComponentName cn) {
parent.mServiceListing.setEnabled(cn, false);
diff --git a/src/com/android/settings/notification/ZenAccessSettings.java b/src/com/android/settings/notification/ZenAccessSettings.java
index f9b1c12..a41a733 100644
--- a/src/com/android/settings/notification/ZenAccessSettings.java
+++ b/src/com/android/settings/notification/ZenAccessSettings.java
@@ -44,9 +44,11 @@
import android.view.View;
import android.widget.Toast;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.overlay.FeatureFactory;
import java.util.ArrayList;
import java.util.Collections;
@@ -171,6 +173,7 @@
}
private static void setAccess(final Context context, final String pkg, final boolean access) {
+ logSpecialPermissionChange(access, pkg, context);
AsyncTask.execute(new Runnable() {
@Override
public void run() {
@@ -180,6 +183,15 @@
});
}
+ @VisibleForTesting
+ static void logSpecialPermissionChange(boolean enable, String packageName, Context context) {
+ int logCategory = enable ? MetricsEvent.APP_SPECIAL_PERMISSION_DND_ALLOW
+ : MetricsEvent.APP_SPECIAL_PERMISSION_DND_DENY;
+ FeatureFactory.getFactory(context).getMetricsFeatureProvider().action(context,
+ logCategory, packageName);
+ }
+
+
private static void deleteRules(final Context context, final String pkg) {
AsyncTask.execute(new Runnable() {
@Override
diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.java b/src/com/android/settings/overlay/FeatureFactoryImpl.java
index 060b58c..18fcaaf 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 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/Android.mk b/tests/robotests/Android.mk
index 0a49be2..3fc034d 100644
--- a/tests/robotests/Android.mk
+++ b/tests/robotests/Android.mk
@@ -14,7 +14,8 @@
LOCAL_JAVA_LIBRARIES := \
junit \
platform-robolectric-prebuilt \
- sdk_vcurrent
+ sdk_vcurrent \
+ telephony-common
LOCAL_INSTRUMENTATION_FOR := Settings
LOCAL_MODULE := SettingsRoboTests
diff --git a/tests/robotests/src/com/android/settings/DeviceAdminAddTest.java b/tests/robotests/src/com/android/settings/DeviceAdminAddTest.java
new file mode 100644
index 0000000..42aed2b
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/DeviceAdminAddTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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;
+
+import android.content.Context;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.testutils.FakeFeatureFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.annotation.Config;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class DeviceAdminAddTest {
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Context mContext;
+ private FakeFeatureFactory mFeatureFactory;
+ private DeviceAdminAdd mDeviceAdminAdd;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ FakeFeatureFactory.setupForTest(mContext);
+ mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
+ mDeviceAdminAdd = Robolectric.buildActivity(DeviceAdminAdd.class).get();
+ }
+
+ @Test
+ public void logSpecialPermissionChange() {
+ mDeviceAdminAdd.logSpecialPermissionChange(true, "app");
+ verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
+ eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_ADMIN_ALLOW), eq("app"));
+
+ mDeviceAdminAdd.logSpecialPermissionChange(false, "app");
+ verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
+ eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_ADMIN_DENY), eq("app"));
+ }
+}
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 0000000..9fc416d
--- /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<UserInfo> 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<UserInfo> 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 615d9ad..aba4a12 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 @@
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 @@
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 @@
| 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/DrawOverlayDetailsTest.java b/tests/robotests/src/com/android/settings/applications/DrawOverlayDetailsTest.java
new file mode 100644
index 0000000..a5306a2
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/DrawOverlayDetailsTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.content.Context;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.telephony.SmsUsageMonitor;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.FakeFeatureFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class DrawOverlayDetailsTest {
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Context mContext;
+
+ private FakeFeatureFactory mFeatureFactory;
+ private DrawOverlayDetails mFragment;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ FakeFeatureFactory.setupForTest(mContext);
+ mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
+ mFragment = new DrawOverlayDetails();
+ mFragment.onAttach(ShadowApplication.getInstance().getApplicationContext());
+ }
+
+ @Test
+ public void logSpecialPermissionChange() {
+ mFragment.logSpecialPermissionChange(true, "app");
+ verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
+ eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_APPDRAW_ALLOW), eq("app"));
+
+ mFragment.logSpecialPermissionChange(false, "app");
+ verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
+ eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_APPDRAW_DENY), eq("app"));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/applications/InstalledAppCounterTest.java b/tests/robotests/src/com/android/settings/applications/InstalledAppCounterTest.java
index 3dc2e9b..8495603 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 @@
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 @@
| 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 @@
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 @@
// 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/applications/PremiumSmsAccessTest.java b/tests/robotests/src/com/android/settings/applications/PremiumSmsAccessTest.java
new file mode 100644
index 0000000..d9c88ff
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/PremiumSmsAccessTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.content.Context;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.telephony.SmsUsageMonitor;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.FakeFeatureFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class PremiumSmsAccessTest {
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Context mContext;
+
+ private FakeFeatureFactory mFeatureFactory;
+ private PremiumSmsAccess mFragment;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ FakeFeatureFactory.setupForTest(mContext);
+ mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
+ mFragment = new PremiumSmsAccess();
+ mFragment.onAttach(ShadowApplication.getInstance().getApplicationContext());
+ }
+
+ @Test
+ public void logSpecialPermissionChange() {
+ mFragment.logSpecialPermissionChange(SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER,
+ "app");
+ verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
+ eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_PREMIUM_SMS_ASK), eq("app"));
+
+ mFragment.logSpecialPermissionChange(SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW,
+ "app");
+ verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
+ eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_PREMIUM_SMS_DENY), eq("app"));
+
+ mFragment.logSpecialPermissionChange(SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW,
+ "app");
+ verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
+ eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_PREMIUM_SMS_ALWAYS_ALLOW),
+ eq("app"));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/applications/UsageAccessDetailsTest.java b/tests/robotests/src/com/android/settings/applications/UsageAccessDetailsTest.java
new file mode 100644
index 0000000..532a923
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/UsageAccessDetailsTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.content.Context;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.FakeFeatureFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class UsageAccessDetailsTest {
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Context mContext;
+
+ private FakeFeatureFactory mFeatureFactory;
+ private UsageAccessDetails mFragment;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ FakeFeatureFactory.setupForTest(mContext);
+ mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
+ mFragment = new UsageAccessDetails();
+ mFragment.onAttach(ShadowApplication.getInstance().getApplicationContext());
+ }
+
+ @Test
+ public void logSpecialPermissionChange() {
+ mFragment.logSpecialPermissionChange(true, "app");
+ verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
+ eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_USAGE_VIEW_ALLOW), eq("app"));
+
+ mFragment.logSpecialPermissionChange(false, "app");
+ verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
+ eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_USAGE_VIEW_DENY), eq("app"));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/applications/VrListenerSettingsTest.java b/tests/robotests/src/com/android/settings/applications/VrListenerSettingsTest.java
new file mode 100644
index 0000000..3abe3f4
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/VrListenerSettingsTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.content.Context;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.FakeFeatureFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class VrListenerSettingsTest {
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Context mContext;
+
+ private FakeFeatureFactory mFeatureFactory;
+ private VrListenerSettings mFragment;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ FakeFeatureFactory.setupForTest(mContext);
+ mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
+ mFragment = new VrListenerSettings();
+ }
+
+ @Test
+ public void logSpecialPermissionChange() {
+ mFragment.logSpecialPermissionChange(true, "app");
+ verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
+ eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_VRHELPER_ALLOW), eq("app"));
+
+ mFragment.logSpecialPermissionChange(false, "app");
+ verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
+ eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_VRHELPER_DENY), eq("app"));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/applications/WriteSettingsDetailsTest.java b/tests/robotests/src/com/android/settings/applications/WriteSettingsDetailsTest.java
new file mode 100644
index 0000000..a632118
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/WriteSettingsDetailsTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.content.Context;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.FakeFeatureFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class WriteSettingsDetailsTest {
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Context mContext;
+
+ private FakeFeatureFactory mFeatureFactory;
+ private WriteSettingsDetails mFragment;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ FakeFeatureFactory.setupForTest(mContext);
+ mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
+ mFragment = new WriteSettingsDetails();
+ }
+
+ @Test
+ public void logSpecialPermissionChange() {
+ mFragment.logSpecialPermissionChange(true, "app");
+ verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
+ eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_SETTINGS_CHANGE_ALLOW),
+ eq("app"));
+
+ mFragment.logSpecialPermissionChange(false, "app");
+ verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
+ eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_SETTINGS_CHANGE_DENY),
+ eq("app"));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/datausage/UnrestrictedDataAccessTest.java b/tests/robotests/src/com/android/settings/datausage/UnrestrictedDataAccessTest.java
index c7b8dee..d674c77 100644
--- a/tests/robotests/src/com/android/settings/datausage/UnrestrictedDataAccessTest.java
+++ b/tests/robotests/src/com/android/settings/datausage/UnrestrictedDataAccessTest.java
@@ -15,31 +15,44 @@
*/
package com.android.settings.datausage;
+import com.android.internal.logging.nano.MetricsProto;
+import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.Process;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
+import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settingslib.applications.ApplicationsState;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class UnrestrictedDataAccessTest {
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Context mContext;
+
@Mock
private ApplicationsState.AppEntry mAppEntry;
private UnrestrictedDataAccess mFragment;
+ private FakeFeatureFactory mFeatureFactory;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ FakeFeatureFactory.setupForTest(mContext);
+ mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
mFragment = new UnrestrictedDataAccess();
}
@@ -59,4 +72,15 @@
assertThat(mFragment.shouldAddPreference(mAppEntry)).isFalse();
}
+ @Test
+ public void logSpecialPermissionChange() {
+ mFragment.logSpecialPermissionChange(true, "app");
+ verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
+ eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_UNL_DATA_ALLOW), eq("app"));
+
+ mFragment.logSpecialPermissionChange(false, "app");
+ verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
+ eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_UNL_DATA_DENY), eq("app"));
+ }
+
}
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 0000000..de24885
--- /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 0000000..1c6f91d
--- /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 0000000..bcaf63f
--- /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 0000000..2bebbf0
--- /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 0000000..610692d
--- /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 9a46e14..3dd1fd7 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 @@
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 5e4e08f..de4d02e 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 @@
final List<PreferenceController> 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 5a0da66..bf2c4ca 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 @@
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/fuelgauge/HighPowerDetailTest.java b/tests/robotests/src/com/android/settings/fuelgauge/HighPowerDetailTest.java
new file mode 100644
index 0000000..a60bc65
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/HighPowerDetailTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.fuelgauge;
+
+import android.content.Context;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.FakeFeatureFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class HighPowerDetailTest {
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Context mContext;
+
+ private FakeFeatureFactory mFeatureFactory;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ FakeFeatureFactory.setupForTest(mContext);
+ mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
+ }
+
+ @Test
+ public void logSpecialPermissionChange() {
+ // Deny means app is whitelisted to opt out of power save restrictions
+ HighPowerDetail.logSpecialPermissionChange(true, "app", mContext);
+ verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
+ eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_BATTERY_DENY), eq("app"));
+
+ // Allow means app is NOT whitelisted to opt out of power save restrictions
+ HighPowerDetail.logSpecialPermissionChange(false, "app", mContext);
+ verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
+ eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_BATTERY_ALLOW), eq("app"));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/NotificationAccessSettingsTest.java b/tests/robotests/src/com/android/settings/notification/NotificationAccessSettingsTest.java
new file mode 100644
index 0000000..b0aa856
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/NotificationAccessSettingsTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.notification;
+
+import android.content.Context;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.FakeFeatureFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class NotificationAccessSettingsTest {
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Context mContext;
+
+ private FakeFeatureFactory mFeatureFactory;
+ private NotificationAccessSettings mFragment;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ FakeFeatureFactory.setupForTest(mContext);
+ mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
+ mFragment = new NotificationAccessSettings();
+ }
+
+ @Test
+ public void logSpecialPermissionChange() {
+ mFragment.logSpecialPermissionChange(true, "app");
+ verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
+ eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_NOTIVIEW_ALLOW),
+ eq("app"));
+
+ mFragment.logSpecialPermissionChange(false, "app");
+ verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
+ eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_NOTIVIEW_DENY),
+ eq("app"));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/ZenAccessSettingsTest.java b/tests/robotests/src/com/android/settings/notification/ZenAccessSettingsTest.java
new file mode 100644
index 0000000..854edcd
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/ZenAccessSettingsTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.notification;
+
+import android.content.Context;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.FakeFeatureFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class ZenAccessSettingsTest {
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Context mContext;
+
+ private FakeFeatureFactory mFeatureFactory;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ FakeFeatureFactory.setupForTest(mContext);
+ mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
+ }
+
+ @Test
+ public void logSpecialPermissionChange() {
+ ZenAccessSettings.logSpecialPermissionChange(true, "app", mContext);
+ verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
+ eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_DND_ALLOW),
+ eq("app"));
+
+ ZenAccessSettings.logSpecialPermissionChange(false, "app", mContext);
+ verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
+ eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_DND_DENY),
+ eq("app"));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/search2/InstalledAppResultLoaderTest.java b/tests/robotests/src/com/android/settings/search2/InstalledAppResultLoaderTest.java
index 24aa94b..8fde73d 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 @@
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 8789928..352d128 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 @@
* 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;
}
}
diff --git a/tests/unit/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroductionTest.java b/tests/unit/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroductionTest.java
new file mode 100644
index 0000000..8afed18
--- /dev/null
+++ b/tests/unit/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroductionTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.fingerprint;
+
+
+import static org.mockito.Mockito.doReturn;
+
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.test.ActivityUnitTestCase;
+import android.view.View;
+import android.widget.Button;
+
+import com.android.settings.R;
+
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class SetupFingerprintEnrollIntroductionTest
+ extends ActivityUnitTestCase<SetupFingerprintEnrollIntroduction> {
+
+ private TestContext mContext;
+
+ @Mock
+ private KeyguardManager mKeyguardManager;
+
+ private SetupFingerprintEnrollIntroduction mActivity;
+
+ public SetupFingerprintEnrollIntroductionTest() {
+ super(SetupFingerprintEnrollIntroduction.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ MockitoAnnotations.initMocks(this);
+ mContext = new TestContext(getInstrumentation().getTargetContext());
+ setActivityContext(mContext);
+
+ getInstrumentation().runOnMainSync(() -> {
+ final Intent intent = new Intent();
+ mActivity = startActivity(intent,
+ null /* savedInstanceState */, null /* lastNonConfigurationInstance */);
+ });
+ }
+
+ public void testKeyguardNotSecure_shouldShowSkipDialog() {
+ doReturn(false).when(mKeyguardManager).isKeyguardSecure();
+
+ getInstrumentation().runOnMainSync(() -> {
+ getInstrumentation().callActivityOnCreate(mActivity, null);
+ getInstrumentation().callActivityOnResume(mActivity);
+
+ final Button skipButton =
+ (Button) mActivity.findViewById(R.id.fingerprint_cancel_button);
+ assertEquals(View.VISIBLE, skipButton.getVisibility());
+ skipButton.performClick();
+ });
+
+ assertFalse(isFinishCalled());
+ }
+
+ public void testKeyguardSecure_shouldNotShowSkipDialog() {
+ doReturn(true).when(mKeyguardManager).isKeyguardSecure();
+
+ getInstrumentation().runOnMainSync(() -> {
+ getInstrumentation().callActivityOnCreate(mActivity, null);
+ getInstrumentation().callActivityOnResume(mActivity);
+
+ final Button skipButton =
+ (Button) mActivity.findViewById(R.id.fingerprint_cancel_button);
+ assertEquals(View.VISIBLE, skipButton.getVisibility());
+ skipButton.performClick();
+ });
+
+ assertTrue(isFinishCalled());
+ }
+
+ public class TestContext extends ContextWrapper {
+
+ public TestContext(Context base) {
+ super(base);
+ }
+
+ @Override
+ public Object getSystemService(String name) {
+ if (Context.KEYGUARD_SERVICE.equals(name)) {
+ return mKeyguardManager;
+ }
+ return super.getSystemService(name);
+ }
+ }
+}