Add Device Owner disclosure to Add Accounts dialog

This CL adds a footer to the Add Accounts dialog that tells the user
when the device is managed by a Device Owner app.

Bug: 32692748
Test: make RunSettingsRoboTests

Change-Id: I0dd161eb0bbbc87c04692d4fa6547bd41dab05e0
This commit is contained in:
Bartosz Fabianowski
2017-01-17 12:42:35 +01:00
parent 5782b390dd
commit ccd4fa8e60
9 changed files with 150 additions and 5 deletions

View File

@@ -8034,6 +8034,14 @@
<string name="enterprise_privacy_always_on_vpn_work">Always-on VPN turned on in your work profile</string> <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] --> <!-- 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> <string name="enterprise_privacy_global_http_proxy">Global HTTP proxy set</string>
<!-- Message indicating that the device is enterprise-managed by a Device Owner [CHAR LIMIT=NONE] -->
<string name="do_disclosure_generic">This device is managed.</string>
<!-- Message indicating that the device is enterprise-managed by a Device Owner [CHAR LIMIT=NONE] -->
<string name="do_disclosure_with_name">This device is managed by <xliff:g id="organization_name" example="Foo, Inc.">%s</xliff:g>.</string>
<!-- Message indicating that the device is enterprise-managed: Space that separates the main text and the "learn more" link that follows it. [CHAR LIMIT=NONE] -->
<string name="do_disclosure_learn_more_separator">" "</string>
<!-- Message indicating that the device is enterprise-managed: Link to learn more about what a Device Owner app can do [CHAR LIMIT=NONE] -->
<string name="do_disclosure_learn_more">Learn more</string>
<!-- Preference label for the Photos & Videos storage section. [CHAR LIMIT=50] --> <!-- Preference label for the Photos & Videos storage section. [CHAR LIMIT=50] -->
<string name="storage_photos_videos">Photos &amp; Videos</string> <string name="storage_photos_videos">Photos &amp; Videos</string>

View File

@@ -22,6 +22,7 @@ import static android.content.Intent.EXTRA_USER;
import android.accounts.AccountManager; import android.accounts.AccountManager;
import android.accounts.AuthenticatorDescription; import android.accounts.AuthenticatorDescription;
import android.app.Activity;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@@ -41,6 +42,10 @@ import com.android.internal.util.CharSequences;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment; import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.Utils; import com.android.settings.Utils;
import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.widget.FooterPreference;
import com.android.settings.widget.FooterPreferenceMixin;
import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
@@ -61,6 +66,10 @@ import java.util.Map;
public class ChooseAccountActivity extends SettingsPreferenceFragment { public class ChooseAccountActivity extends SettingsPreferenceFragment {
private static final String TAG = "ChooseAccountActivity"; private static final String TAG = "ChooseAccountActivity";
private EnterprisePrivacyFeatureProvider mFeatureProvider;
private FooterPreference mEnterpriseDisclosurePreference = null;
private String[] mAuthorities; private String[] mAuthorities;
private PreferenceGroup mAddAccountGroup; private PreferenceGroup mAddAccountGroup;
private final ArrayList<ProviderEntry> mProviderList = new ArrayList<ProviderEntry>(); private final ArrayList<ProviderEntry> mProviderList = new ArrayList<ProviderEntry>();
@@ -101,6 +110,10 @@ public class ChooseAccountActivity extends SettingsPreferenceFragment {
public void onCreate(Bundle icicle) { public void onCreate(Bundle icicle) {
super.onCreate(icicle); super.onCreate(icicle);
final Activity activity = getActivity();
mFeatureProvider = FeatureFactory.getFactory(activity)
.getEnterprisePrivacyFeatureProvider(activity);
addPreferencesFromResource(R.xml.add_account_settings); addPreferencesFromResource(R.xml.add_account_settings);
mAuthorities = getIntent().getStringArrayExtra( mAuthorities = getIntent().getStringArrayExtra(
AccountPreferenceBase.AUTHORITIES_FILTER_KEY); AccountPreferenceBase.AUTHORITIES_FILTER_KEY);
@@ -187,6 +200,7 @@ public class ChooseAccountActivity extends SettingsPreferenceFragment {
p.checkAccountManagementAndSetDisabled(mUserHandle.getIdentifier()); p.checkAccountManagementAndSetDisabled(mUserHandle.getIdentifier());
mAddAccountGroup.addPreference(p); mAddAccountGroup.addPreference(p);
} }
addEnterpriseDisclosure();
} else { } else {
if (Log.isLoggable(TAG, Log.VERBOSE)) { if (Log.isLoggable(TAG, Log.VERBOSE)) {
final StringBuilder auths = new StringBuilder(); final StringBuilder auths = new StringBuilder();
@@ -201,6 +215,19 @@ public class ChooseAccountActivity extends SettingsPreferenceFragment {
} }
} }
private void addEnterpriseDisclosure() {
final CharSequence disclosure = mFeatureProvider.getDeviceOwnerDisclosure(getActivity());
if (disclosure == null) {
return;
}
if (mEnterpriseDisclosurePreference == null) {
mEnterpriseDisclosurePreference = mFooterPreferenceMixin.createFooterPreference();
mEnterpriseDisclosurePreference.setSelectable(false);
}
mEnterpriseDisclosurePreference.setTitle(disclosure);
mAddAccountGroup.addPreference(mEnterpriseDisclosurePreference);
}
public ArrayList<String> getAuthoritiesForAccountType(String type) { public ArrayList<String> getAuthoritiesForAccountType(String type) {
if (mAccountTypeToAuthorities == null) { if (mAccountTypeToAuthorities == null) {
mAccountTypeToAuthorities = Maps.newHashMap(); mAccountTypeToAuthorities = Maps.newHashMap();

View File

@@ -33,6 +33,13 @@ public interface DevicePolicyManagerWrapper {
*/ */
ComponentName getDeviceOwnerComponentOnAnyUser(); ComponentName getDeviceOwnerComponentOnAnyUser();
/**
* Calls {@code DevicePolicyManager.getDeviceOwnerNameOnAnyUser()}.
*
* @see android.app.admin.DevicePolicyManager#getDeviceOwnerNameOnAnyUser
*/
public CharSequence getDeviceOwnerOrganizationName();
/** /**
* Calls {@code DevicePolicyManager.getPermissionGrantState()}. * Calls {@code DevicePolicyManager.getPermissionGrantState()}.
* *

View File

@@ -32,6 +32,11 @@ public class DevicePolicyManagerWrapperImpl implements DevicePolicyManagerWrappe
return mDpm.getDeviceOwnerComponentOnAnyUser(); return mDpm.getDeviceOwnerComponentOnAnyUser();
} }
@Override
public CharSequence getDeviceOwnerOrganizationName() {
return mDpm.getDeviceOwnerOrganizationName();
}
@Override @Override
public int getPermissionGrantState(@Nullable ComponentName admin, String packageName, public int getPermissionGrantState(@Nullable ComponentName admin, String packageName,
String permission) { String permission) {

View File

@@ -16,6 +16,8 @@
package com.android.settings.enterprise; package com.android.settings.enterprise;
import android.content.Context;
import java.util.Date; import java.util.Date;
public interface EnterprisePrivacyFeatureProvider { public interface EnterprisePrivacyFeatureProvider {
@@ -31,6 +33,15 @@ public interface EnterprisePrivacyFeatureProvider {
*/ */
boolean isInCompMode(); boolean isInCompMode();
/**
* Returns a message informing the user that the device is managed by a Device Owner app. The
* message includes a Learn More link that takes the user to the enterprise privacy section of
* Settings. If the device is not managed by a Device Owner app, returns {@code null}.
*
* @param context The context in which to show the enterprise privacy section of Settings
*/
CharSequence getDeviceOwnerDisclosure(Context context);
/** /**
* Returns the time at which the Device Owner last retrieved security logs, or {@code null} if * Returns the time at which the Device Owner last retrieved security logs, or {@code null} if
* logs were never retrieved by the Device Owner on this device. * logs were never retrieved by the Device Owner on this device.

View File

@@ -16,11 +16,19 @@
package com.android.settings.enterprise; package com.android.settings.enterprise;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.UserInfo; import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import android.provider.Settings;
import android.text.SpannableStringBuilder;
import android.text.style.ClickableSpan;
import android.view.View;
import com.android.settings.R;
import com.android.settings.applications.PackageManagerWrapper; import com.android.settings.applications.PackageManagerWrapper;
import com.android.settings.vpn2.ConnectivityManagerWrapper; import com.android.settings.vpn2.ConnectivityManagerWrapper;
import com.android.settings.vpn2.VpnUtils; import com.android.settings.vpn2.VpnUtils;
@@ -34,15 +42,18 @@ public class EnterprisePrivacyFeatureProviderImpl implements EnterprisePrivacyFe
private final PackageManagerWrapper mPm; private final PackageManagerWrapper mPm;
private final UserManager mUm; private final UserManager mUm;
private final ConnectivityManagerWrapper mCm; private final ConnectivityManagerWrapper mCm;
private final Resources mResources;
private static final int MY_USER_ID = UserHandle.myUserId(); private static final int MY_USER_ID = UserHandle.myUserId();
public EnterprisePrivacyFeatureProviderImpl(DevicePolicyManagerWrapper dpm, public EnterprisePrivacyFeatureProviderImpl(DevicePolicyManagerWrapper dpm,
PackageManagerWrapper pm, UserManager um, ConnectivityManagerWrapper cm) { PackageManagerWrapper pm, UserManager um, ConnectivityManagerWrapper cm,
Resources resources) {
mDpm = dpm; mDpm = dpm;
mPm = pm; mPm = pm;
mUm = um; mUm = um;
mCm = cm; mCm = cm;
mResources = resources;
} }
@Override @Override
@@ -67,6 +78,26 @@ public class EnterprisePrivacyFeatureProviderImpl implements EnterprisePrivacyFe
return hasDeviceOwner() && getManagedProfileUserId() != -1; return hasDeviceOwner() && getManagedProfileUserId() != -1;
} }
@Override
public CharSequence getDeviceOwnerDisclosure(Context context) {
if (!hasDeviceOwner()) {
return null;
}
final SpannableStringBuilder disclosure = new SpannableStringBuilder();
final CharSequence organizationName = mDpm.getDeviceOwnerOrganizationName();
if (organizationName != null) {
disclosure.append(mResources.getString(R.string.do_disclosure_with_name,
organizationName));
} else {
disclosure.append(mResources.getString(R.string.do_disclosure_generic));
}
disclosure.append(mResources.getString(R.string.do_disclosure_learn_more_separator));
disclosure.append(mResources.getString(R.string.do_disclosure_learn_more),
new EnterprisePrivacySpan(context), 0);
return disclosure;
}
@Override @Override
public Date getLastSecurityLogRetrievalTime() { public Date getLastSecurityLogRetrievalTime() {
final long timestamp = mDpm.getLastSecurityLogRetrievalTime(); final long timestamp = mDpm.getLastSecurityLogRetrievalTime();
@@ -101,4 +132,23 @@ public class EnterprisePrivacyFeatureProviderImpl implements EnterprisePrivacyFe
public boolean isGlobalHttpProxySet() { public boolean isGlobalHttpProxySet() {
return mCm.getGlobalProxy() != null; return mCm.getGlobalProxy() != null;
} }
protected static class EnterprisePrivacySpan extends ClickableSpan {
private final Context mContext;
public EnterprisePrivacySpan(Context context) {
mContext = context;
}
@Override
public void onClick(View widget) {
mContext.startActivity(new Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS));
}
@Override
public boolean equals(Object object) {
return object instanceof EnterprisePrivacySpan
&& ((EnterprisePrivacySpan) object).mContext == mContext;
}
}
} }

View File

@@ -111,7 +111,8 @@ public class FeatureFactoryImpl extends FeatureFactory {
new PackageManagerWrapperImpl(context.getPackageManager()), new PackageManagerWrapperImpl(context.getPackageManager()),
UserManager.get(context), UserManager.get(context),
new ConnectivityManagerWrapperImpl((ConnectivityManager) context new ConnectivityManagerWrapperImpl((ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE))); .getSystemService(Context.CONNECTIVITY_SERVICE)),
context.getResources());
} }
return mEnterprisePrivacyFeatureProvider; return mEnterprisePrivacyFeatureProvider;
} }

View File

@@ -198,7 +198,7 @@ public final class InstalledAppCounterTest {
} }
} }
private class IsLaunchIntentFor extends ArgumentMatcher<Intent> { private static class IsLaunchIntentFor extends ArgumentMatcher<Intent> {
private final String mPackageName; private final String mPackageName;
IsLaunchIntentFor(String packageName) { IsLaunchIntentFor(String packageName) {
@@ -211,7 +211,7 @@ public final class InstalledAppCounterTest {
if (intent == null) { if (intent == null) {
return false; return false;
} }
if (intent.getAction() != Intent.ACTION_MAIN) { if (!Intent.ACTION_MAIN.equals(intent.getAction())) {
return false; return false;
} }
final Set<String> categories = intent.getCategories(); final Set<String> categories = intent.getCategories();

View File

@@ -17,12 +17,16 @@
package com.android.settings.enterprise; package com.android.settings.enterprise;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.UserInfo; import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.net.ProxyInfo; import android.net.ProxyInfo;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import android.text.SpannableStringBuilder;
import com.android.settings.R;
import com.android.settings.SettingsRobolectricTestRunner; import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig; import com.android.settings.TestConfig;
import com.android.settings.applications.PackageManagerWrapper; import com.android.settings.applications.PackageManagerWrapper;
@@ -34,12 +38,14 @@ import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
/** /**
@@ -50,6 +56,7 @@ import static org.mockito.Mockito.when;
public final class EnterprisePrivacyFeatureProviderImplTest { public final class EnterprisePrivacyFeatureProviderImplTest {
private final ComponentName DEVICE_OWNER = new ComponentName("dummy", "component"); private final ComponentName DEVICE_OWNER = new ComponentName("dummy", "component");
private final String DEVICE_OWNER_ORGANIZATION = new String("ACME");
private final Date TIMESTAMP = new Date(2011, 11, 11); private final Date TIMESTAMP = new Date(2011, 11, 11);
private final int MY_USER_ID = UserHandle.myUserId(); private final int MY_USER_ID = UserHandle.myUserId();
private final int MANAGED_PROFILE_USER_ID = MY_USER_ID + 1; private final int MANAGED_PROFILE_USER_ID = MY_USER_ID + 1;
@@ -61,6 +68,7 @@ public final class EnterprisePrivacyFeatureProviderImplTest {
private @Mock PackageManagerWrapper mPackageManager; private @Mock PackageManagerWrapper mPackageManager;
private @Mock UserManager mUserManager; private @Mock UserManager mUserManager;
private @Mock ConnectivityManagerWrapper mConnectivityManger; private @Mock ConnectivityManagerWrapper mConnectivityManger;
private Resources mResources;
private EnterprisePrivacyFeatureProvider mProvider; private EnterprisePrivacyFeatureProvider mProvider;
@@ -72,9 +80,10 @@ public final class EnterprisePrivacyFeatureProviderImplTest {
.thenReturn(true); .thenReturn(true);
when(mUserManager.getProfiles(MY_USER_ID)).thenReturn(mProfiles); when(mUserManager.getProfiles(MY_USER_ID)).thenReturn(mProfiles);
mProfiles.add(new UserInfo(MY_USER_ID, "", "", 0 /* flags */)); mProfiles.add(new UserInfo(MY_USER_ID, "", "", 0 /* flags */));
mResources = ShadowApplication.getInstance().getApplicationContext().getResources();
mProvider = new EnterprisePrivacyFeatureProviderImpl(mDevicePolicyManager, mPackageManager, mProvider = new EnterprisePrivacyFeatureProviderImpl(mDevicePolicyManager, mPackageManager,
mUserManager, mConnectivityManger); mUserManager, mConnectivityManger, mResources);
} }
@Test @Test
@@ -95,6 +104,33 @@ public final class EnterprisePrivacyFeatureProviderImplTest {
assertThat(mProvider.isInCompMode()).isTrue(); assertThat(mProvider.isInCompMode()).isTrue();
} }
@Test
public void testGetDeviceOwnerDisclosure() {
final Context context = mock(Context.class);
when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()).thenReturn(null);
assertThat(mProvider.getDeviceOwnerDisclosure(context)).isNull();
SpannableStringBuilder disclosure = new SpannableStringBuilder();
disclosure.append(mResources.getString(R.string.do_disclosure_generic));
disclosure.append(mResources.getString(R.string.do_disclosure_learn_more_separator));
disclosure.append(mResources.getString(R.string.do_disclosure_learn_more),
new EnterprisePrivacyFeatureProviderImpl.EnterprisePrivacySpan(context), 0);
when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()).thenReturn(DEVICE_OWNER);
when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null);
assertThat(mProvider.getDeviceOwnerDisclosure(context)).isEqualTo(disclosure);
disclosure = new SpannableStringBuilder();
disclosure.append(mResources.getString(R.string.do_disclosure_with_name,
DEVICE_OWNER_ORGANIZATION));
disclosure.append(mResources.getString(R.string.do_disclosure_learn_more_separator));
disclosure.append(mResources.getString(R.string.do_disclosure_learn_more),
new EnterprisePrivacyFeatureProviderImpl.EnterprisePrivacySpan(context), 0);
when(mDevicePolicyManager.getDeviceOwnerOrganizationName())
.thenReturn(DEVICE_OWNER_ORGANIZATION);
assertThat(mProvider.getDeviceOwnerDisclosure(context)).isEqualTo(disclosure);
}
@Test @Test
public void testGetLastSecurityLogRetrievalTime() { public void testGetLastSecurityLogRetrievalTime() {
when(mDevicePolicyManager.getLastSecurityLogRetrievalTime()).thenReturn(-1L); when(mDevicePolicyManager.getLastSecurityLogRetrievalTime()).thenReturn(-1L);