Update Quick Settings footer and dialog text for a financed device

Bug: 173826319
Bug: 158157476
Test: Used a test device that is registered via ZT
Test: atest SystemUITests:com.android.systemui.qs.QSSecurityFooterTest

Change-Id: Iff3630c3d4cb640115d04a6b8775a58db8d7ca6a
This commit is contained in:
Salud Lemus
2021-02-26 22:43:47 +00:00
parent 9857a76c09
commit a9ad16f112
8 changed files with 191 additions and 10 deletions

View File

@@ -38,7 +38,6 @@
android:id="@+id/device_management_subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/monitoring_title_device_owned"
style="@style/DeviceManagementDialogTitle"
android:paddingBottom="@dimen/qs_footer_dialog_subtitle_padding"
/>

View File

@@ -1259,6 +1259,9 @@
<!-- Disclosure at the bottom of Quick Settings that indicates that the user's device belongs to their organization, and the organization can monitor network traffic on that device. The placeholder is the organization's name. [CHAR LIMIT=100] -->
<string name="quick_settings_disclosure_named_management_monitoring"><xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g> owns this device and may monitor network traffic</string>
<!-- Disclosure at the bottom of Quick Settings that indicates that the user's financed device belongs to the Creditor. The placeholder is the Creditor's name. [CHAR LIMIT=100] -->
<string name="quick_settings_financed_disclosure_named_management">This device is provided by <xliff:g id="organization_name" example="Foo, Inc.">%s</xliff:g></string>
<!-- Disclosure at the bottom of Quick Settings that indicates that the user's device belongs to their organization, and the device is connected to a VPN. The placeholder is the VPN name. [CHAR LIMIT=100] -->
<string name="quick_settings_disclosure_management_named_vpn">This device belongs to your organization and is connected to <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g></string>
@@ -1298,6 +1301,9 @@
<!-- Disclosure at the bottom of Quick Settings that indicates that the device is connected to a VPN. The placeholder is the VPN name. [CHAR LIMIT=100] -->
<string name="quick_settings_disclosure_named_vpn">This device is connected to <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g></string>
<!-- Monitoring dialog title for financed device [CHAR LIMIT=60] -->
<string name="monitoring_title_financed_device">This device is provided by <xliff:g id="organization_name" example="Foo, Inc.">%s</xliff:g></string>
<!-- Monitoring dialog title for device owned devices [CHAR LIMIT=35] -->
<string name="monitoring_title_device_owned">Device management</string>
@@ -1332,6 +1338,9 @@
<!-- Dialog that a user can access via Quick Settings. The dialog describes what the IT admin can monitor (and the changes they can make) on the user's device. [CHAR LIMIT=NONE]-->
<string name="monitoring_description_named_management">This device belongs to <xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g>.\n\nYour IT admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nFor more information, contact your IT admin.</string>
<!-- Dialog that a user can access via Quick Settings. The dialog describes what a Creditor can monitor (and the changes they can make) on the user's financed device. [CHAR LIMIT=NONE]-->
<string name="monitoring_financed_description_named_management"><xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g> may be able to access data associated with this device and change this device\s settings.\n\nIf you have questions, contact <xliff:g id="organization_name" example="Foo, Inc.">%2$s</xliff:g>.</string>
<!-- Dialog that a user can access via Quick Settings. The dialog describes what the IT admin can monitor (and the changes they can make) on the user's device. [CHAR LIMIT=NONE]-->
<string name="monitoring_description_management">This device belongs to your organization.\n\nYour IT admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nFor more information, contact your IT admin.</string>

View File

@@ -15,6 +15,8 @@
*/
package com.android.systemui.qs;
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
import static com.android.systemui.qs.dagger.QSFragmentModule.QS_SECURITY_FOOTER_VIEW;
import android.app.AlertDialog;
@@ -244,8 +246,14 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
if (organizationName == null) {
return mContext.getString(R.string.quick_settings_disclosure_management);
}
return mContext.getString(R.string.quick_settings_disclosure_named_management,
organizationName);
if (isFinancedDevice()) {
return mContext.getString(
R.string.quick_settings_financed_disclosure_named_management,
organizationName);
} else {
return mContext.getString(R.string.quick_settings_disclosure_named_management,
organizationName);
}
} // end if(isDeviceManaged)
if (hasCACertsInWorkProfile) {
if (workProfileOrganizationName == null) {
@@ -355,6 +363,10 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
.inflate(R.layout.quick_settings_footer_dialog, null, false);
// device management section
TextView deviceManagementSubtitle =
dialogView.findViewById(R.id.device_management_subtitle);
deviceManagementSubtitle.setText(getManagementTitle(deviceOwnerOrganization));
CharSequence managementMessage = getManagementMessage(isDeviceManaged,
deviceOwnerOrganization, isProfileOwnerOfOrganizationOwnedDevice,
workProfileOrganizationName);
@@ -468,7 +480,8 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
}
}
private String getSettingsButton() {
@VisibleForTesting
String getSettingsButton() {
return mContext.getString(R.string.monitoring_button_view_policies);
}
@@ -490,8 +503,13 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
return null;
}
if (isDeviceManaged && organizationName != null) {
return mContext.getString(
R.string.monitoring_description_named_management, organizationName);
if (isFinancedDevice()) {
return mContext.getString(R.string.monitoring_financed_description_named_management,
organizationName, organizationName);
} else {
return mContext.getString(
R.string.monitoring_description_named_management, organizationName);
}
} else if (isProfileOwnerOfOrganizationOwnedDevice && workProfileOrganizationName != null) {
return mContext.getString(
R.string.monitoring_description_named_management, workProfileOrganizationName);
@@ -557,14 +575,23 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
return message;
}
private int getTitle(String deviceOwner) {
if (deviceOwner != null) {
return R.string.monitoring_title_device_owned;
@VisibleForTesting
CharSequence getManagementTitle(CharSequence deviceOwnerOrganization) {
if (deviceOwnerOrganization != null && isFinancedDevice()) {
return mContext.getString(R.string.monitoring_title_financed_device,
deviceOwnerOrganization);
} else {
return R.string.monitoring_title;
return mContext.getString(R.string.monitoring_title_device_owned);
}
}
private boolean isFinancedDevice() {
return mSecurityController.isDeviceManaged()
&& mSecurityController.getDeviceOwnerType(
mSecurityController.getDeviceOwnerComponentOnAnyUser())
== DEVICE_OWNER_TYPE_FINANCED;
}
private final Runnable mUpdateIcon = new Runnable() {
@Override
public void run() {

View File

@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.policy;
import android.app.admin.DeviceAdminInfo;
import android.content.ComponentName;
import android.graphics.drawable.Drawable;
import com.android.systemui.Dumpable;
@@ -33,6 +34,10 @@ public interface SecurityController extends CallbackController<SecurityControlle
String getProfileOwnerName();
CharSequence getDeviceOwnerOrganizationName();
CharSequence getWorkProfileOrganizationName();
/** Device owner component even if not on this user. **/
ComponentName getDeviceOwnerComponentOnAnyUser();
/** Device owner type for a device owner. **/
int getDeviceOwnerType(ComponentName admin);
boolean isNetworkLoggingEnabled();
boolean isVpnEnabled();
boolean isVpnRestricted();

View File

@@ -15,9 +15,11 @@
*/
package com.android.systemui.statusbar.policy;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.admin.DeviceAdminInfo;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManager.DeviceOwnerType;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -224,6 +226,18 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi
return null;
}
@Override
@Nullable
public ComponentName getDeviceOwnerComponentOnAnyUser() {
return mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser();
}
@Override
@DeviceOwnerType
public int getDeviceOwnerType(@NonNull ComponentName admin) {
return mDevicePolicyManager.getDeviceOwnerType(admin);
}
@Override
public boolean isNetworkLoggingEnabled() {
return mDevicePolicyManager.isNetworkLoggingEnabled(null);

View File

@@ -14,6 +14,9 @@
package com.android.systemui.qs;
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
@@ -24,6 +27,8 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.pm.UserInfo;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.VectorDrawable;
@@ -74,6 +79,8 @@ public class QSSecurityFooterTest extends SysuiTestCase {
private final String VPN_PACKAGE = "TestVPN";
private final String VPN_PACKAGE_2 = "TestVPN 2";
private static final String PARENTAL_CONTROLS_LABEL = "Parental Control App";
private static final ComponentName DEVICE_OWNER_COMPONENT =
new ComponentName("TestDPC", "Test");
private ViewGroup mRootView;
private TextView mFooterText;
@@ -101,6 +108,11 @@ public class QSSecurityFooterTest extends SysuiTestCase {
mFooterIcon = mRootView.findViewById(R.id.footer_icon);
mPrimaryFooterIcon = mRootView.findViewById(R.id.primary_footer_icon);
mFooter.setHostEnvironment(null);
when(mSecurityController.getDeviceOwnerComponentOnAnyUser())
.thenReturn(DEVICE_OWNER_COMPONENT);
when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
.thenReturn(DEVICE_OWNER_TYPE_DEFAULT);
}
@Test
@@ -147,6 +159,27 @@ public class QSSecurityFooterTest extends SysuiTestCase {
assertEquals(-1, mFooterIcon.getLastImageResource());
}
@Test
public void testManagedFinancedDeviceWithOwnerName() {
when(mSecurityController.isDeviceManaged()).thenReturn(true);
when(mSecurityController.getDeviceOwnerOrganizationName())
.thenReturn(MANAGING_ORGANIZATION);
when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
.thenReturn(DEVICE_OWNER_TYPE_FINANCED);
mFooter.refreshState();
TestableLooper.get(this).processAllMessages();
assertEquals(mContext.getString(
R.string.quick_settings_financed_disclosure_named_management,
MANAGING_ORGANIZATION), mFooterText.getText());
assertEquals(View.VISIBLE, mRootView.getVisibility());
assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility());
// -1 == never set.
assertEquals(-1, mFooterIcon.getLastImageResource());
}
@Test
public void testManagedDemoMode() {
when(mSecurityController.isDeviceManaged()).thenReturn(true);
@@ -382,6 +415,25 @@ public class QSSecurityFooterTest extends SysuiTestCase {
mFooterText.getText());
}
@Test
public void testGetManagementTitleForNonFinancedDevice() {
when(mSecurityController.isDeviceManaged()).thenReturn(true);
assertEquals(mContext.getString(R.string.monitoring_title_device_owned),
mFooter.getManagementTitle(MANAGING_ORGANIZATION));
}
@Test
public void testGetManagementTitleForFinancedDevice() {
when(mSecurityController.isDeviceManaged()).thenReturn(true);
when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
.thenReturn(DEVICE_OWNER_TYPE_FINANCED);
assertEquals(mContext.getString(R.string.monitoring_title_financed_device,
MANAGING_ORGANIZATION),
mFooter.getManagementTitle(MANAGING_ORGANIZATION));
}
@Test
public void testGetManagementMessage_noManagement() {
assertEquals(null, mFooter.getManagementMessage(
@@ -408,6 +460,21 @@ public class QSSecurityFooterTest extends SysuiTestCase {
/* workProfileOrganizationName= */ null));
}
@Test
public void testGetManagementMessage_deviceOwner_asFinancedDevice() {
when(mSecurityController.isDeviceManaged()).thenReturn(true);
when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
.thenReturn(DEVICE_OWNER_TYPE_FINANCED);
assertEquals(mContext.getString(R.string.monitoring_financed_description_named_management,
MANAGING_ORGANIZATION, MANAGING_ORGANIZATION),
mFooter.getManagementMessage(
/* isDeviceManaged= */ true,
MANAGING_ORGANIZATION,
/* isProfileOwnerOfOrganizationOwnedDevice= */ false,
/* workProfileOrganizationName= */ null));
}
@Test
public void testGetManagementMessage_profileOwnerOfOrganizationOwnedDevice() {
assertEquals(mContext.getString(R.string.monitoring_description_named_management,
@@ -587,6 +654,34 @@ public class QSSecurityFooterTest extends SysuiTestCase {
assertEquals(PARENTAL_CONTROLS_LABEL, textView.getText());
}
@Test
public void testCreateDialogViewForFinancedDevice() {
when(mSecurityController.isDeviceManaged()).thenReturn(true);
when(mSecurityController.getDeviceOwnerOrganizationName())
.thenReturn(MANAGING_ORGANIZATION);
when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
.thenReturn(DEVICE_OWNER_TYPE_FINANCED);
// Initialize AlertDialog which sets the text for the negative button, which is used when
// creating the dialog for a financed device.
mFooter.showDeviceMonitoringDialog();
// The above statement would display the Quick Settings dialog which requires user input,
// so simulate the press to continue with the unit test (otherwise, it is stuck).
mFooter.onClick(null, DialogInterface.BUTTON_NEGATIVE);
View view = mFooter.createDialogView();
TextView managementSubtitle = view.findViewById(R.id.device_management_subtitle);
assertEquals(View.VISIBLE, managementSubtitle.getVisibility());
assertEquals(mContext.getString(R.string.monitoring_title_financed_device,
MANAGING_ORGANIZATION), managementSubtitle.getText());
TextView managementMessage = view.findViewById(R.id.device_management_warning);
assertEquals(View.VISIBLE, managementMessage.getVisibility());
assertEquals(mContext.getString(R.string.monitoring_financed_description_named_management,
MANAGING_ORGANIZATION, MANAGING_ORGANIZATION), managementMessage.getText());
assertEquals(mContext.getString(R.string.monitoring_button_view_policies),
mFooter.getSettingsButton());
}
private CharSequence addLink(CharSequence description) {
final SpannableStringBuilder message = new SpannableStringBuilder();
message.append(description);

View File

@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.policy;
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -62,6 +64,9 @@ import java.util.List;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class SecurityControllerTest extends SysuiTestCase {
private static final ComponentName DEVICE_OWNER_COMPONENT =
new ComponentName("com.android.foo", "bar");
private final DevicePolicyManager mDevicePolicyManager = mock(DevicePolicyManager.class);
private final IKeyChainService.Stub mKeyChainService = mock(IKeyChainService.Stub.class);
private final UserManager mUserManager = mock(UserManager.class);
@@ -126,6 +131,22 @@ public class SecurityControllerTest extends SysuiTestCase {
assertEquals("organization", mSecurityController.getDeviceOwnerOrganizationName());
}
@Test
public void testGetDeviceOwnerComponentOnAnyUser() {
when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser())
.thenReturn(DEVICE_OWNER_COMPONENT);
assertEquals(mSecurityController.getDeviceOwnerComponentOnAnyUser(),
DEVICE_OWNER_COMPONENT);
}
@Test
public void testGetDeviceOwnerType() {
when(mDevicePolicyManager.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
.thenReturn(DEVICE_OWNER_TYPE_FINANCED);
assertEquals(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT),
DEVICE_OWNER_TYPE_FINANCED);
}
@Test
public void testWorkAccount() throws Exception {
assertFalse(mSecurityController.hasCACertInCurrentUser());

View File

@@ -15,6 +15,7 @@
package com.android.systemui.utils.leaks;
import android.app.admin.DeviceAdminInfo;
import android.content.ComponentName;
import android.graphics.drawable.Drawable;
import android.testing.LeakCheck;
@@ -67,6 +68,16 @@ public class FakeSecurityController extends BaseLeakChecker<SecurityControllerCa
return null;
}
@Override
public ComponentName getDeviceOwnerComponentOnAnyUser() {
return null;
}
@Override
public int getDeviceOwnerType(ComponentName admin) {
return 0;
}
@Override
public boolean isNetworkLoggingEnabled() {
return false;