Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9fcd1b83 authored by Jason Parks's avatar Jason Parks Committed by Android (Google) Code Review
Browse files

Merge "Parental Controls disclaimer"

parents 1968c20b 8e179a40
Loading
Loading
Loading
Loading
+60 −0
Original line number Original line Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
** Copyright 2020, 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.
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/scrollView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:clipToPadding="false">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="?android:attr/dialogPreferredPadding"
        android:paddingRight="?android:attr/dialogPreferredPadding"
        android:paddingLeft="?android:attr/dialogPreferredPadding"
        android:orientation="vertical">
        <ImageView
            android:id="@+id/parental_controls_icon"
            android:layout_width="36dip"
            android:layout_height="36dip"
            android:layout_gravity="center_horizontal"

        />
        <LinearLayout
            android:id="@+id/parental_controls_disclosures"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingBottom="?android:attr/dialogPreferredPadding"
            android:orientation="vertical">
            <TextView
                android:id="@+id/parental_controls_title"
                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"
            />
            <TextView
                android:id="@+id/parental_controls_warning"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textAppearance="@style/TextAppearance.DeviceManagementDialog.Content"
                android:text="@string/monitoring_description_parental_controls"
            />
        </LinearLayout>
    </LinearLayout>
</ScrollView>
+10 −0
Original line number Original line Diff line number Diff line
@@ -1286,6 +1286,9 @@
    <!-- Footer vpn present text [CHAR LIMIT=50] -->
    <!-- Footer vpn present text [CHAR LIMIT=50] -->
    <string name="branded_vpn_footer">Network may be monitored</string>
    <string name="branded_vpn_footer">Network may be monitored</string>


    <!-- Disclosure at the bottom of Quick Settings that indicates that parental controls are enabled. [CHAR LIMIT=100] -->
    <string name="quick_settings_disclosure_parental_controls">This device is managed by your parent</string>

    <!-- 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. [CHAR LIMIT=100] -->
    <!-- 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. [CHAR LIMIT=100] -->
    <string name="quick_settings_disclosure_management_monitoring">Your organization owns this device and may monitor network traffic</string>
    <string name="quick_settings_disclosure_management_monitoring">Your organization owns this device and may monitor network traffic</string>


@@ -1359,6 +1362,9 @@
    <!-- Monitoring dialog label for button opening a page with more information on the admin's abilities [CHAR LIMIT=30] -->
    <!-- Monitoring dialog label for button opening a page with more information on the admin's abilities [CHAR LIMIT=30] -->
    <string name="monitoring_button_view_policies">View Policies</string>
    <string name="monitoring_button_view_policies">View Policies</string>


    <!-- Monitoring dialog label for button opening a page with more information on parental controls [CHAR LIMIT=30] -->
    <string name="monitoring_button_view_controls">View controls</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]-->
    <!-- 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>
    <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>


@@ -1428,6 +1434,10 @@
    <!-- Monitoring dialog VPN with profile owner text [CHAR LIMIT=400] -->
    <!-- Monitoring dialog VPN with profile owner text [CHAR LIMIT=400] -->
    <string name="monitoring_description_vpn_profile_owned">Your work profile is managed by <xliff:g id="organization">%1$s</xliff:g>.\n\nYour admin is capable of monitoring your network activity including emails, apps, and websites.\n\nFor more information, contact your admin.\n\nYou\'re also connected to a VPN, which can monitor your network activity.</string>
    <string name="monitoring_description_vpn_profile_owned">Your work profile is managed by <xliff:g id="organization">%1$s</xliff:g>.\n\nYour admin is capable of monitoring your network activity including emails, apps, and websites.\n\nFor more information, contact your admin.\n\nYou\'re also connected to a VPN, which can monitor your network activity.</string>


    <!-- Dialog that a user can access via Quick Settings. [CHAR LIMIT=NONE]-->
    <string name="monitoring_description_parental_controls">This device is managed by your parent. Your parent can see and manage information such as the apps you use, your location, and your screen time.</string>


    <!-- Name for a generic legacy VPN connection [CHAR LIMIT=20] -->
    <!-- Name for a generic legacy VPN connection [CHAR LIMIT=20] -->
    <string name="legacy_vpn_name">VPN</string>
    <string name="legacy_vpn_name">VPN</string>


+60 −10
Original line number Original line Diff line number Diff line
@@ -16,11 +16,13 @@
package com.android.systemui.qs;
package com.android.systemui.qs;


import android.app.AlertDialog;
import android.app.AlertDialog;
import android.app.admin.DeviceAdminInfo;
import android.app.admin.DevicePolicyEventLogger;
import android.app.admin.DevicePolicyEventLogger;
import android.content.Context;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.Intent;
import android.content.pm.UserInfo;
import android.content.pm.UserInfo;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Handler;
import android.os.Looper;
import android.os.Looper;
import android.os.Message;
import android.os.Message;
@@ -39,6 +41,8 @@ import android.view.Window;
import android.widget.ImageView;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.TextView;


import androidx.annotation.VisibleForTesting;

import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.FrameworkStatsLog;
import com.android.systemui.Dependency;
import com.android.systemui.Dependency;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.FontSizeUtils;
@@ -156,15 +160,16 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
                mSecurityController.getWorkProfileOrganizationName();
                mSecurityController.getWorkProfileOrganizationName();
        final boolean isProfileOwnerOfOrganizationOwnedDevice =
        final boolean isProfileOwnerOfOrganizationOwnedDevice =
                mSecurityController.isProfileOwnerOfOrganizationOwnedDevice();
                mSecurityController.isProfileOwnerOfOrganizationOwnedDevice();
        final boolean isParentalControlsEnabled = mSecurityController.isParentalControlsEnabled();
        // Update visibility of footer
        // Update visibility of footer
        mIsVisible = (isDeviceManaged && !isDemoDevice) || hasCACerts || hasCACertsInWorkProfile
        mIsVisible = (isDeviceManaged && !isDemoDevice) || hasCACerts || hasCACertsInWorkProfile
                || vpnName != null || vpnNameWorkProfile != null
                || vpnName != null || vpnNameWorkProfile != null
                || isProfileOwnerOfOrganizationOwnedDevice;
                || isProfileOwnerOfOrganizationOwnedDevice || isParentalControlsEnabled;
        // Update the string
        // Update the string
        mFooterTextContent = getFooterText(isDeviceManaged, hasWorkProfile,
        mFooterTextContent = getFooterText(isDeviceManaged, hasWorkProfile,
                hasCACerts, hasCACertsInWorkProfile, isNetworkLoggingEnabled, vpnName,
                hasCACerts, hasCACertsInWorkProfile, isNetworkLoggingEnabled, vpnName,
                vpnNameWorkProfile, organizationName, workProfileOrganizationName,
                vpnNameWorkProfile, organizationName, workProfileOrganizationName,
                isProfileOwnerOfOrganizationOwnedDevice);
                isProfileOwnerOfOrganizationOwnedDevice, isParentalControlsEnabled);
        // Update the icon
        // Update the icon
        int footerIconId = R.drawable.ic_info_outline;
        int footerIconId = R.drawable.ic_info_outline;
        if (vpnName != null || vpnNameWorkProfile != null) {
        if (vpnName != null || vpnNameWorkProfile != null) {
@@ -185,7 +190,10 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
            boolean hasCACerts, boolean hasCACertsInWorkProfile, boolean isNetworkLoggingEnabled,
            boolean hasCACerts, boolean hasCACertsInWorkProfile, boolean isNetworkLoggingEnabled,
            String vpnName, String vpnNameWorkProfile, CharSequence organizationName,
            String vpnName, String vpnNameWorkProfile, CharSequence organizationName,
            CharSequence workProfileOrganizationName,
            CharSequence workProfileOrganizationName,
            boolean isProfileOwnerOfOrganizationOwnedDevice) {
            boolean isProfileOwnerOfOrganizationOwnedDevice, boolean isParentalControlsEnabled) {
        if (isParentalControlsEnabled) {
            return mContext.getString(R.string.quick_settings_disclosure_parental_controls);
        }
        if (isDeviceManaged || DEBUG_FORCE_VISIBLE) {
        if (isDeviceManaged || DEBUG_FORCE_VISIBLE) {
            if (hasCACerts || hasCACertsInWorkProfile || isNetworkLoggingEnabled) {
            if (hasCACerts || hasCACertsInWorkProfile || isNetworkLoggingEnabled) {
                if (organizationName == null) {
                if (organizationName == null) {
@@ -268,6 +276,27 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
    }
    }


    private void createDialog() {
    private void createDialog() {
        mDialog = new SystemUIDialog(mContext);
        mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this);
        mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getNegativeButton(), this);

        mDialog.setView(createDialogView());

        mDialog.show();
        mDialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
    }

    @VisibleForTesting
    View createDialogView() {
        if (mSecurityController.isParentalControlsEnabled()) {
            return createParentalControlsDialogView();
        }
        return createOrganizationDialogView();
    }

    private View createOrganizationDialogView() {
        final boolean isDeviceManaged = mSecurityController.isDeviceManaged();
        final boolean isDeviceManaged = mSecurityController.isDeviceManaged();
        boolean isProfileOwnerOfOrganizationOwnedDevice =
        boolean isProfileOwnerOfOrganizationOwnedDevice =
                mSecurityController.isProfileOwnerOfOrganizationOwnedDevice();
                mSecurityController.isProfileOwnerOfOrganizationOwnedDevice();
@@ -282,13 +311,10 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
        final String vpnName = mSecurityController.getPrimaryVpnName();
        final String vpnName = mSecurityController.getPrimaryVpnName();
        final String vpnNameWorkProfile = mSecurityController.getWorkProfileVpnName();
        final String vpnNameWorkProfile = mSecurityController.getWorkProfileVpnName();


        mDialog = new SystemUIDialog(mContext);

        mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        View dialogView = LayoutInflater.from(
        View dialogView = LayoutInflater.from(
                new ContextThemeWrapper(mContext, R.style.Theme_SystemUI_Dialog))
                new ContextThemeWrapper(mContext, R.style.Theme_SystemUI_Dialog))
                .inflate(R.layout.quick_settings_footer_dialog, null, false);
                .inflate(R.layout.quick_settings_footer_dialog, null, false);
        mDialog.setView(dialogView);
        mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this);


        // device management section
        // device management section
        CharSequence managementMessage = getManagementMessage(isDeviceManaged,
        CharSequence managementMessage = getManagementMessage(isDeviceManaged,
@@ -353,9 +379,26 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
                vpnMessage != null,
                vpnMessage != null,
                dialogView);
                dialogView);


        mDialog.show();
        return dialogView;
        mDialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT,
    }
                ViewGroup.LayoutParams.WRAP_CONTENT);

    private View createParentalControlsDialogView() {
        View dialogView = LayoutInflater.from(
                new ContextThemeWrapper(mContext, R.style.Theme_SystemUI_Dialog))
                .inflate(R.layout.quick_settings_footer_dialog_parental_controls, null, false);

        DeviceAdminInfo info = mSecurityController.getDeviceAdminInfo();
        Drawable icon = mSecurityController.getIcon(info);
        if (icon != null) {
            ImageView imageView = (ImageView) dialogView.findViewById(R.id.parental_controls_icon);
            imageView.setImageDrawable(icon);
        }

        TextView parentalControlsTitle =
                (TextView) dialogView.findViewById(R.id.parental_controls_title);
        parentalControlsTitle.setText(mSecurityController.getLabel(info));

        return dialogView;
    }
    }


    protected void configSubtitleVisibility(boolean showDeviceManagement, boolean showCaCerts,
    protected void configSubtitleVisibility(boolean showDeviceManagement, boolean showCaCerts,
@@ -394,6 +437,13 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
        return mContext.getString(R.string.ok);
        return mContext.getString(R.string.ok);
    }
    }


    private String getNegativeButton() {
        if (mSecurityController.isParentalControlsEnabled()) {
            return mContext.getString(R.string.monitoring_button_view_controls);
        }
        return null;
    }

    protected CharSequence getManagementMessage(boolean isDeviceManaged,
    protected CharSequence getManagementMessage(boolean isDeviceManaged,
            CharSequence organizationName, boolean isProfileOwnerOfOrganizationOwnedDevice,
            CharSequence organizationName, boolean isProfileOwnerOfOrganizationOwnedDevice,
            CharSequence workProfileOrganizationName) {
            CharSequence workProfileOrganizationName) {
+12 −0
Original line number Original line Diff line number Diff line
@@ -15,6 +15,9 @@
 */
 */
package com.android.systemui.statusbar.policy;
package com.android.systemui.statusbar.policy;


import android.app.admin.DeviceAdminInfo;
import android.graphics.drawable.Drawable;

import com.android.systemui.Dumpable;
import com.android.systemui.Dumpable;
import com.android.systemui.statusbar.policy.SecurityController.SecurityControllerCallback;
import com.android.systemui.statusbar.policy.SecurityController.SecurityControllerCallback;


@@ -40,6 +43,15 @@ public interface SecurityController extends CallbackController<SecurityControlle
    boolean hasCACertInCurrentUser();
    boolean hasCACertInCurrentUser();
    boolean hasCACertInWorkProfile();
    boolean hasCACertInWorkProfile();
    void onUserSwitched(int newUserId);
    void onUserSwitched(int newUserId);
    /** Whether or not parental controls is enabled */
    boolean isParentalControlsEnabled();
    /** DeviceAdminInfo for active admin */
    DeviceAdminInfo getDeviceAdminInfo();
    /** Icon for admin */
    Drawable getIcon(DeviceAdminInfo info);
    /** Label for admin */
    CharSequence getLabel(DeviceAdminInfo info);



    public interface SecurityControllerCallback {
    public interface SecurityControllerCallback {
        void onStateChanged();
        void onStateChanged();
+51 −0
Original line number Original line Diff line number Diff line
@@ -16,15 +16,19 @@
package com.android.systemui.statusbar.policy;
package com.android.systemui.statusbar.policy;


import android.app.ActivityManager;
import android.app.ActivityManager;
import android.app.admin.DeviceAdminInfo;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.pm.UserInfo;
import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.IConnectivityManager;
import android.net.IConnectivityManager;
@@ -53,7 +57,10 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.settings.CurrentUserTracker;


import org.xmlpull.v1.XmlPullParserException;

import java.io.FileDescriptor;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.concurrent.Executor;
import java.util.concurrent.Executor;
@@ -306,6 +313,50 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi
        fireCallbacks();
        fireCallbacks();
    }
    }


    @Override
    public boolean isParentalControlsEnabled() {
        return getProfileOwnerOrDeviceOwnerSupervisionComponent() != null;
    }

    @Override
    public DeviceAdminInfo getDeviceAdminInfo() {
        return getDeviceAdminInfo(getProfileOwnerOrDeviceOwnerComponent());
    }

    @Override
    public Drawable getIcon(DeviceAdminInfo info) {
        return (info == null) ? null : info.loadIcon(mPackageManager);
    }

    @Override
    public CharSequence getLabel(DeviceAdminInfo info) {
        return (info == null) ? null : info.loadLabel(mPackageManager);
    }

    private ComponentName getProfileOwnerOrDeviceOwnerSupervisionComponent() {
        UserHandle currentUser = new UserHandle(mCurrentUserId);
        return mDevicePolicyManager
               .getProfileOwnerOrDeviceOwnerSupervisionComponent(currentUser);
    }

    // Returns the ComponentName of the current DO/PO. Right now it only checks the supervision
    // component but can be changed to check for other DO/POs. This change would make getIcon()
    // and getLabel() work for all admins.
    private ComponentName getProfileOwnerOrDeviceOwnerComponent() {
        return getProfileOwnerOrDeviceOwnerSupervisionComponent();
    }

    private DeviceAdminInfo getDeviceAdminInfo(ComponentName componentName) {
        try {
            ResolveInfo resolveInfo = new ResolveInfo();
            resolveInfo.activityInfo = mPackageManager.getReceiverInfo(componentName,
                    PackageManager.GET_META_DATA);
            return new DeviceAdminInfo(mContext, resolveInfo);
        } catch (NameNotFoundException | XmlPullParserException | IOException e) {
            return null;
        }
    }

    private void refreshCACerts(int userId) {
    private void refreshCACerts(int userId) {
        mBgExecutor.execute(() -> {
        mBgExecutor.execute(() -> {
            Pair<Integer, Boolean> idWithCert = null;
            Pair<Integer, Boolean> idWithCert = null;
Loading