Clean up DeviceAdminSetting: make it into DashboardFragment

Change-Id: Ib5634f782daef15ab317175084c6813d6b8a8bb7
Fixes: 110207366
Test: robo
This commit is contained in:
Fan Zhang
2018-06-14 13:17:51 -07:00
parent 85a639577b
commit fc7188d822
20 changed files with 689 additions and 726 deletions

View File

@@ -1,42 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2011 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.
-->
<!-- Based on simple_list_item_2.xml in framework -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeight"
android:orientation="vertical"
android:paddingStart="@dimen/preference_no_icon_padding_start"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
<TextView
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceListItem" />
<TextView
android:id="@android:id/text2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary" />
</LinearLayout>

View File

@@ -1,86 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 2010, 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.
*/
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:minHeight="?android:attr/listPreferredItemHeight"
android:orientation="horizontal"
android:gravity="center_vertical" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start|center_vertical"
android:minWidth="60dp"
android:orientation="horizontal"
android:paddingEnd="12dp"
android:paddingTop="4dp"
android:paddingBottom="4dp">
<ImageView
android:id="@+id/icon"
android:layout_width="@dimen/secondary_app_icon_size"
android:layout_height="@dimen/secondary_app_icon_size"
android:layout_gravity="center_vertical"
android:layout_marginEnd="8dip"
android:contentDescription="@null" />
</LinearLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dip"
android:layout_marginBottom="8dip"
android:layout_weight="1">
<TextView android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceMedium"
android:ellipsize="marquee"
android:layout_alignParentTop="true"
android:fadingEdge="horizontal" />
<TextView android:id="@+id/description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dip"
android:layout_below="@id/name"
android:layout_alignStart="@id/name"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"
android:maxLines="4" />
</RelativeLayout>
<Switch
android:id="@+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="16dip"
android:focusable="false"
android:clickable="false" />
</LinearLayout>

View File

@@ -1,43 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1">
<ListView android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbarStyle="outsideOverlay"
android:drawSelectorOnTop="false"
android:fastScrollEnabled="true" />
<TextView android:id="@android:id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/no_device_admins"
android:textAppearance="?android:attr/textAppearanceMedium" />
</FrameLayout>
</LinearLayout>

View File

@@ -96,9 +96,6 @@
<!-- Whether touch_sounds should be shown or not. --> <!-- Whether touch_sounds should be shown or not. -->
<bool name="config_show_touch_sounds">true</bool> <bool name="config_show_touch_sounds">true</bool>
<!-- Whether device_administrators should be shown or not. -->
<bool name="config_show_device_administrators">true</bool>
<!-- Whether encryption_and_credentials_encryption_status should be shown or not. --> <!-- Whether encryption_and_credentials_encryption_status should be shown or not. -->
<bool name="config_show_encryption_and_credentials_encryption_status">true</bool> <bool name="config_show_encryption_and_credentials_encryption_status">true</bool>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2018 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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/manage_device_admin"
android:key="device_admin_settings">
<PreferenceCategory
android:key="device_admin_list"
android:title="@string/summary_placeholder"
settings:controller="com.android.settings.applications.specialaccess.deviceadmin.DeviceAdminListPreferenceController" />
</PreferenceScreen>

View File

@@ -48,7 +48,7 @@
android:key="fingerprint_settings" android:key="fingerprint_settings"
android:title="@string/security_settings_fingerprint_preference_title" android:title="@string/security_settings_fingerprint_preference_title"
android:summary="@string/summary_placeholder" android:summary="@string/summary_placeholder"
settings:keywords="@string/keywords_fingerprint_settings"/> settings:keywords="@string/keywords_fingerprint_settings" />
</PreferenceCategory> </PreferenceCategory>
@@ -75,7 +75,7 @@
android:key="visiblepattern_profile" android:key="visiblepattern_profile"
android:summary="@string/summary_placeholder" android:summary="@string/summary_placeholder"
android:title="@string/lockpattern_settings_enable_visible_pattern_title_profile" android:title="@string/lockpattern_settings_enable_visible_pattern_title_profile"
settings:controller="com.android.settings.security.VisiblePatternProfilePreferenceController"/> settings:controller="com.android.settings.security.VisiblePatternProfilePreferenceController" />
<Preference <Preference
android:key="fingerprint_settings_profile" android:key="fingerprint_settings_profile"
@@ -100,7 +100,7 @@
android:key="show_password" android:key="show_password"
android:title="@string/show_password" android:title="@string/show_password"
android:summary="@string/show_password_summary" android:summary="@string/show_password_summary"
settings:controller="com.android.settings.security.ShowPasswordPreferenceController"/> settings:controller="com.android.settings.security.ShowPasswordPreferenceController" />
</PreferenceCategory> </PreferenceCategory>
@@ -112,7 +112,8 @@
android:key="manage_device_admin" android:key="manage_device_admin"
android:title="@string/manage_device_admin" android:title="@string/manage_device_admin"
android:summary="@string/summary_placeholder" android:summary="@string/summary_placeholder"
android:fragment="com.android.settings.applications.specialaccess.deviceadmin.DeviceAdminSettings" /> android:fragment="com.android.settings.applications.specialaccess.deviceadmin.DeviceAdminSettings"
settings:controller="com.android.settings.enterprise.ManageDeviceAdminPreferenceController" />
<Preference <Preference
android:key="enterprise_privacy" android:key="enterprise_privacy"

View File

@@ -35,7 +35,8 @@
android:key="device_administrators" android:key="device_administrators"
android:title="@string/manage_device_admin" android:title="@string/manage_device_admin"
android:fragment="com.android.settings.applications.specialaccess.deviceadmin.DeviceAdminSettings" android:fragment="com.android.settings.applications.specialaccess.deviceadmin.DeviceAdminSettings"
settings:controller="com.android.settings.applications.specialaccess.deviceadmin.DeviceAdministratorsController" /> android:summary="@string/summary_placeholder"
settings:controller="com.android.settings.enterprise.ManageDeviceAdminPreferenceController" />
<Preference <Preference
android:key="system_alert_window" android:key="system_alert_window"

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) 2018 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.specialaccess.deviceadmin;
import android.app.admin.DeviceAdminInfo;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.util.Log;
class DeviceAdminListItem implements Comparable<DeviceAdminListItem> {
private static final String TAG = "DeviceAdminListItem";
private final String mKey;
private final DeviceAdminInfo mInfo;
private final CharSequence mName;
private final Drawable mIcon;
private final DevicePolicyManager mDPM;
private CharSequence mDescription;
public DeviceAdminListItem(Context context, DeviceAdminInfo info) {
mInfo = info;
mKey = mInfo.getComponent().flattenToString();
mDPM = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
final PackageManager pm = context.getPackageManager();
mName = mInfo.loadLabel(pm);
try {
mDescription = mInfo.loadDescription(pm);
} catch (Resources.NotFoundException exception) {
Log.w(TAG, "Setting description to null because can't find resource: " + mKey);
}
mIcon = pm.getUserBadgedIcon(mInfo.loadIcon(pm),
new UserHandle(DeviceAdminUtils.getUserIdFromDeviceAdminInfo(mInfo)));
}
@Override
public int compareTo(DeviceAdminListItem other) {
return this.mName.toString().compareTo(other.mName.toString());
}
public String getKey() {
return mKey;
}
public CharSequence getName() {
return mName;
}
public CharSequence getDescription() {
return mDescription;
}
public boolean isActive() {
return mDPM.isAdminActiveAsUser(mInfo.getComponent(),
DeviceAdminUtils.getUserIdFromDeviceAdminInfo(mInfo));
}
public Drawable getIcon() {
return mIcon;
}
public boolean isEnabled() {
return !mDPM.isRemovingAdmin(mInfo.getComponent(),
DeviceAdminUtils.getUserIdFromDeviceAdminInfo(mInfo));
}
public UserHandle getUser() {
return new UserHandle(DeviceAdminUtils.getUserIdFromDeviceAdminInfo(mInfo));
}
public Intent getLaunchIntent(Context context) {
return new Intent(context, DeviceAdminAdd.class)
.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mInfo.getComponent());
}
}

View File

@@ -0,0 +1,296 @@
/*
* Copyright (C) 2018 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.specialaccess.deviceadmin;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
import android.app.AppGlobals;
import android.app.admin.DeviceAdminInfo;
import android.app.admin.DeviceAdminReceiver;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
import com.android.settingslib.widget.FooterPreference;
import com.android.settingslib.widget.FooterPreferenceMixin;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
public class DeviceAdminListPreferenceController extends BasePreferenceController
implements LifecycleObserver, OnStart, OnStop {
private static final IntentFilter FILTER = new IntentFilter();
private static final String TAG = "DeviceAdminListPrefCtrl";
private final DevicePolicyManager mDPM;
private final UserManager mUm;
private final PackageManager mPackageManager;
private final IPackageManager mIPackageManager;
/**
* Internal collection of device admin info objects for all profiles associated with the current
* user.
*/
private final ArrayList<DeviceAdminListItem> mAdmins = new ArrayList<>();
private final SparseArray<ComponentName> mProfileOwnerComponents = new SparseArray<>();
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// Refresh the list, if state change has been received. It could be that checkboxes
// need to be updated
if (TextUtils.equals(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED, intent.getAction())) {
updateList();
}
}
};
private PreferenceGroup mPreferenceGroup;
private FooterPreferenceMixin mFooterPreferenceMixin;
static {
FILTER.addAction(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
}
public DeviceAdminListPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
mDPM = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
mPackageManager = mContext.getPackageManager();
mIPackageManager = AppGlobals.getPackageManager();
}
public DeviceAdminListPreferenceController setFooterPreferenceMixin(
FooterPreferenceMixin mixin) {
mFooterPreferenceMixin = mixin;
return this;
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreferenceGroup = (PreferenceGroup) screen.findPreference(getPreferenceKey());
}
@Override
public void onStart() {
mContext.registerReceiverAsUser(
mBroadcastReceiver, UserHandle.ALL, FILTER,
null /* broadcastPermission */, null /* scheduler */);
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
mProfileOwnerComponents.clear();
final List<UserHandle> profiles = mUm.getUserProfiles();
final int profilesSize = profiles.size();
for (int i = 0; i < profilesSize; ++i) {
final int profileId = profiles.get(i).getIdentifier();
mProfileOwnerComponents.put(profileId, mDPM.getProfileOwnerAsUser(profileId));
}
updateList();
}
@Override
public void onStop() {
mContext.unregisterReceiver(mBroadcastReceiver);
}
@VisibleForTesting
void updateList() {
refreshData();
refreshUI();
}
private void refreshData() {
mAdmins.clear();
final List<UserHandle> profiles = mUm.getUserProfiles();
for (UserHandle profile : profiles) {
final int profileId = profile.getIdentifier();
updateAvailableAdminsForProfile(profileId);
}
Collections.sort(mAdmins);
}
private void refreshUI() {
if (mPreferenceGroup == null) {
return;
}
if (mFooterPreferenceMixin != null) {
final FooterPreference footer = mFooterPreferenceMixin.createFooterPreference();
footer.setTitle(R.string.no_device_admins);
footer.setVisible(mAdmins.isEmpty());
}
final Map<String, SwitchPreference> preferenceCache = new ArrayMap<>();
final Context prefContext = mPreferenceGroup.getContext();
final int childrenCount = mPreferenceGroup.getPreferenceCount();
for (int i = 0; i < childrenCount; i++) {
SwitchPreference pref = (SwitchPreference) mPreferenceGroup.getPreference(i);
preferenceCache.put(pref.getKey(), pref);
}
for (DeviceAdminListItem item : mAdmins) {
final String key = item.getKey();
SwitchPreference pref = preferenceCache.remove(key);
if (pref == null) {
pref = new SwitchPreference(prefContext);
mPreferenceGroup.addPreference(pref);
}
bindPreference(item, pref);
}
for (SwitchPreference unusedCacheItem : preferenceCache.values()) {
mPreferenceGroup.removePreference(unusedCacheItem);
}
}
private void bindPreference(DeviceAdminListItem item, SwitchPreference pref) {
pref.setKey(item.getKey());
pref.setTitle(item.getName());
pref.setIcon(item.getIcon());
pref.setChecked(item.isActive());
pref.setSummary(item.getDescription());
pref.setEnabled(item.isEnabled());
pref.setOnPreferenceClickListener(preference -> {
final UserHandle user = item.getUser();
mContext.startActivityAsUser(item.getLaunchIntent(mContext), user);
return true;
});
pref.setOnPreferenceChangeListener((preference, newValue) -> false);
}
/**
* Add device admins to the internal collection that belong to a profile.
*
* @param profileId the profile identifier.
*/
private void updateAvailableAdminsForProfile(final int profileId) {
// We are adding the union of two sets 'A' and 'B' of device admins to mAvailableAdmins.
// - Set 'A' is the set of active admins for the profile
// - set 'B' is the set of listeners to DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED for
// the profile.
// Add all of set 'A' to mAvailableAdmins.
final List<ComponentName> activeAdminsForProfile = mDPM.getActiveAdminsAsUser(profileId);
addActiveAdminsForProfile(activeAdminsForProfile, profileId);
// Collect set 'B' and add B-A to mAvailableAdmins.
addDeviceAdminBroadcastReceiversForProfile(activeAdminsForProfile, profileId);
}
/**
* Add a {@link DeviceAdminInfo} object to the internal collection of available admins for all
* active admin components associated with a profile.
*/
private void addActiveAdminsForProfile(List<ComponentName> activeAdmins, int profileId) {
if (activeAdmins == null) {
return;
}
for (ComponentName activeAdmin : activeAdmins) {
final ActivityInfo ai;
try {
ai = mIPackageManager.getReceiverInfo(activeAdmin,
PackageManager.GET_META_DATA |
PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS |
PackageManager.MATCH_DIRECT_BOOT_UNAWARE |
PackageManager.MATCH_DIRECT_BOOT_AWARE, profileId);
} catch (RemoteException e) {
Log.w(TAG, "Unable to load component: " + activeAdmin);
continue;
}
final DeviceAdminInfo deviceAdminInfo = DeviceAdminUtils.createDeviceAdminInfo(
mContext, ai);
if (deviceAdminInfo == null) {
continue;
}
mAdmins.add(new DeviceAdminListItem(mContext, deviceAdminInfo));
}
}
/**
* Add a profile's device admins that are receivers of
* {@code DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED} to the internal collection if they
* haven't been added yet.
*
* @param alreadyAddedComponents the set of active admin component names. Receivers of
* {@code DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED}
* whose component is in this
* set are not added to the internal collection again.
* @param profileId the identifier of the profile
*/
private void addDeviceAdminBroadcastReceiversForProfile(
Collection<ComponentName> alreadyAddedComponents, int profileId) {
final List<ResolveInfo> enabledForProfile = mPackageManager.queryBroadcastReceiversAsUser(
new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED),
PackageManager.GET_META_DATA | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS,
profileId);
if (enabledForProfile == null) {
return;
}
for (ResolveInfo resolveInfo : enabledForProfile) {
final ComponentName riComponentName =
new ComponentName(resolveInfo.activityInfo.packageName,
resolveInfo.activityInfo.name);
if (alreadyAddedComponents != null
&& alreadyAddedComponents.contains(riComponentName)) {
continue;
}
DeviceAdminInfo deviceAdminInfo = DeviceAdminUtils.createDeviceAdminInfo(
mContext, resolveInfo.activityInfo);
// add only visible ones (note: active admins are added regardless of visibility)
if (deviceAdminInfo != null && deviceAdminInfo.isVisible()) {
if (!deviceAdminInfo.getActivityInfo().applicationInfo.isInternal()) {
continue;
}
mAdmins.add(new DeviceAdminListItem(mContext, deviceAdminInfo));
}
}
}
}

View File

@@ -16,448 +16,55 @@
package com.android.settings.applications.specialaccess.deviceadmin; package com.android.settings.applications.specialaccess.deviceadmin;
import android.app.Activity;
import android.app.AppGlobals;
import android.app.ListFragment;
import android.app.admin.DeviceAdminInfo;
import android.app.admin.DeviceAdminReceiver;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.provider.SearchIndexableResource;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.Switch;
import android.widget.TextView;
import com.android.internal.logging.nano.MetricsProto; import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.Utils; import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.instrumentation.Instrumentable; import com.android.settings.search.Indexable;
import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin; import com.android.settingslib.search.SearchIndexable;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
public class DeviceAdminSettings extends ListFragment implements Instrumentable { @SearchIndexable
public class DeviceAdminSettings extends DashboardFragment {
static final String TAG = "DeviceAdminSettings"; static final String TAG = "DeviceAdminSettings";
private VisibilityLoggerMixin mVisibilityLoggerMixin;
private DevicePolicyManager mDPM;
private UserManager mUm;
private static class DeviceAdminListItem implements Comparable<DeviceAdminListItem> {
public DeviceAdminInfo info;
// These aren't updated so they keep a stable sort order if user activates / de-activates
// an admin.
public String name;
public boolean active;
public int compareTo(DeviceAdminListItem other) {
// Sort active admins first, then by name.
if (this.active != other.active) {
return this.active ? -1 : 1;
}
return this.name.compareTo(other.name);
}
}
/**
* Internal collection of device admin info objects for all profiles associated with the current
* user.
*/
private final ArrayList<DeviceAdminListItem>
mAdmins = new ArrayList<DeviceAdminListItem>();
private String mDeviceOwnerPkg;
private SparseArray<ComponentName> mProfileOwnerComponents = new SparseArray<ComponentName>();
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// Refresh the list, if state change has been received. It could be that checkboxes
// need to be updated
if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(
intent.getAction())) {
updateList();
}
}
};
@Override
public int getMetricsCategory() { public int getMetricsCategory() {
return MetricsProto.MetricsEvent.DEVICE_ADMIN_SETTINGS; return MetricsProto.MetricsEvent.DEVICE_ADMIN_SETTINGS;
} }
@Override @Override
public void onCreate(Bundle icicle) { public void onAttach(Context context) {
super.onCreate(icicle); super.onAttach(context);
mVisibilityLoggerMixin = new VisibilityLoggerMixin(getMetricsCategory(), use(DeviceAdminListPreferenceController.class).setFooterPreferenceMixin(
FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider()); mFooterPreferenceMixin);
} }
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, protected int getPreferenceScreenResId() {
Bundle savedInstanceState) { return R.xml.device_admin_settings;
mDPM = (DevicePolicyManager) getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE);
mUm = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
return inflater.inflate(R.layout.device_admin_settings, container, false);
} }
@Override @Override
public void onActivityCreated(Bundle savedInstanceState) { protected String getLogTag() {
super.onActivityCreated(savedInstanceState); return TAG;
setHasOptionsMenu(true);
Utils.forceCustomPadding(getListView(), true /* additive padding */);
getActivity().setTitle(R.string.manage_device_admin);
} }
public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider() {
@Override @Override
public void onResume() { public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
super.onResume(); boolean enabled) {
final Activity activity = getActivity(); final ArrayList<SearchIndexableResource> result = new ArrayList<>();
mVisibilityLoggerMixin.onResume();
IntentFilter filter = new IntentFilter();
filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
activity.registerReceiverAsUser(
mBroadcastReceiver, UserHandle.ALL, filter, null, null);
final ComponentName deviceOwnerComponent = mDPM.getDeviceOwnerComponentOnAnyUser(); final SearchIndexableResource sir = new SearchIndexableResource(context);
mDeviceOwnerPkg = sir.xmlResId = R.xml.device_admin_settings;
deviceOwnerComponent != null ? deviceOwnerComponent.getPackageName() : null; result.add(sir);
mProfileOwnerComponents.clear(); return result;
final List<UserHandle> profiles = mUm.getUserProfiles();
final int profilesSize = profiles.size();
for (int i = 0; i < profilesSize; ++i) {
final int profileId = profiles.get(i).getIdentifier();
mProfileOwnerComponents.put(profileId, mDPM.getProfileOwnerAsUser(profileId));
}
updateList();
}
@Override
public void onPause() {
final Activity activity = getActivity();
activity.unregisterReceiver(mBroadcastReceiver);
mVisibilityLoggerMixin.onPause();
super.onPause();
}
/**
* Update the internal collection of available admins for all profiles associated with the
* current user.
*/
void updateList() {
mAdmins.clear();
final List<UserHandle> profiles = mUm.getUserProfiles();
final int profilesSize = profiles.size();
for (int i = 0; i < profilesSize; ++i) {
final int profileId = profiles.get(i).getIdentifier();
updateAvailableAdminsForProfile(profileId);
}
Collections.sort(mAdmins);
getListView().setAdapter(new PolicyListAdapter());
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
Object o = l.getAdapter().getItem(position);
DeviceAdminInfo dpi = (DeviceAdminInfo) o;
final UserHandle user = new UserHandle(getUserId(dpi));
final Activity activity = getActivity();
Intent intent = new Intent(activity, DeviceAdminAdd.class);
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, dpi.getComponent());
activity.startActivityAsUser(intent, user);
}
static class ViewHolder {
ImageView icon;
TextView name;
Switch checkbox;
TextView description;
}
class PolicyListAdapter extends BaseAdapter {
final LayoutInflater mInflater;
PolicyListAdapter() {
mInflater = (LayoutInflater)
getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public boolean hasStableIds() {
return false;
}
@Override
public int getCount() {
return mAdmins.size();
}
/**
* The item for the given position in the list.
*
* @return DeviceAdminInfo object for actual device admins.
*/
@Override
public Object getItem(int position) {
return ((DeviceAdminListItem) (mAdmins.get(position))).info;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public boolean areAllItemsEnabled() {
return false;
}
/**
* See {@link #getItemViewType} for the view types.
*/
@Override
public int getViewTypeCount() {
return 1;
}
/**
* Returns 0 for all types.
*/
@Override
public int getItemViewType(int position) {
return 0;
}
@Override
public boolean isEnabled(int position) {
Object o = getItem(position);
return isEnabled(o);
}
private boolean isEnabled(Object o) {
DeviceAdminInfo info = (DeviceAdminInfo) o;
// Disable item if admin is being removed
if (isRemovingAdmin(info)) {
return false;
}
return true;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Object o = getItem(position);
if (convertView == null) {
convertView = newDeviceAdminView(parent);
}
bindView(convertView, (DeviceAdminInfo) o);
return convertView;
}
private View newDeviceAdminView(ViewGroup parent) {
View v = mInflater.inflate(R.layout.device_admin_item, parent, false);
ViewHolder h = new ViewHolder();
h.icon = v.findViewById(R.id.icon);
h.name = v.findViewById(R.id.name);
h.checkbox = v.findViewById(R.id.checkbox);
h.description = v.findViewById(R.id.description);
v.setTag(h);
return v;
}
private void bindView(View view, DeviceAdminInfo item) {
final Activity activity = getActivity();
ViewHolder vh = (ViewHolder) view.getTag();
Drawable activityIcon = item.loadIcon(activity.getPackageManager());
Drawable badgedIcon = activity.getPackageManager().getUserBadgedIcon(
activityIcon, new UserHandle(getUserId(item)));
vh.icon.setImageDrawable(badgedIcon);
vh.name.setText(item.loadLabel(activity.getPackageManager()));
vh.checkbox.setChecked(isActiveAdmin(item));
final boolean enabled = isEnabled(item);
try {
vh.description.setText(item.loadDescription(activity.getPackageManager()));
} catch (Resources.NotFoundException e) {
}
vh.checkbox.setEnabled(enabled);
vh.name.setEnabled(enabled);
vh.description.setEnabled(enabled);
vh.icon.setEnabled(enabled);
}
}
private boolean isDeviceOwner(DeviceAdminInfo item) {
return getUserId(item) == UserHandle.myUserId()
&& item.getPackageName().equals(mDeviceOwnerPkg);
}
private boolean isProfileOwner(DeviceAdminInfo item) {
ComponentName profileOwner = mProfileOwnerComponents.get(getUserId(item));
return item.getComponent().equals(profileOwner);
}
private boolean isActiveAdmin(DeviceAdminInfo item) {
return mDPM.isAdminActiveAsUser(item.getComponent(), getUserId(item));
}
private boolean isRemovingAdmin(DeviceAdminInfo item) {
return mDPM.isRemovingAdmin(item.getComponent(), getUserId(item));
}
/**
* Add device admins to the internal collection that belong to a profile.
*
* @param profileId the profile identifier.
*/
private void updateAvailableAdminsForProfile(final int profileId) {
// We are adding the union of two sets 'A' and 'B' of device admins to mAvailableAdmins.
// Set 'A' is the set of active admins for the profile whereas set 'B' is the set of
// listeners to DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED for the profile.
// Add all of set 'A' to mAvailableAdmins.
List<ComponentName> activeAdminsListForProfile = mDPM.getActiveAdminsAsUser(profileId);
addActiveAdminsForProfile(activeAdminsListForProfile, profileId);
// Collect set 'B' and add B-A to mAvailableAdmins.
addDeviceAdminBroadcastReceiversForProfile(activeAdminsListForProfile, profileId);
}
/**
* Add a profile's device admins that are receivers of
* {@code DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED} to the internal collection if they
* haven't been added yet.
*
* @param alreadyAddedComponents the set of active admin component names. Receivers of
* {@code DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED} whose component is in this
* set are not added to the internal collection again.
* @param profileId the identifier of the profile
*/
private void addDeviceAdminBroadcastReceiversForProfile(
Collection<ComponentName> alreadyAddedComponents, final int profileId) {
final PackageManager pm = getActivity().getPackageManager();
List<ResolveInfo> enabledForProfile = pm.queryBroadcastReceiversAsUser(
new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED),
PackageManager.GET_META_DATA | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
profileId);
if (enabledForProfile == null) {
enabledForProfile = Collections.emptyList();
}
final int n = enabledForProfile.size();
for (int i = 0; i < n; ++i) {
ResolveInfo resolveInfo = enabledForProfile.get(i);
ComponentName riComponentName =
new ComponentName(resolveInfo.activityInfo.packageName,
resolveInfo.activityInfo.name);
if (alreadyAddedComponents == null
|| !alreadyAddedComponents.contains(riComponentName)) {
DeviceAdminInfo deviceAdminInfo = createDeviceAdminInfo(resolveInfo.activityInfo);
// add only visible ones (note: active admins are added regardless of visibility)
if (deviceAdminInfo != null && deviceAdminInfo.isVisible()) {
if (!deviceAdminInfo.getActivityInfo().applicationInfo.isInternal()) {
continue;
}
DeviceAdminListItem item = new DeviceAdminListItem();
item.info = deviceAdminInfo;
item.name = deviceAdminInfo.loadLabel(pm).toString();
// Active ones already added.
item.active = false;
mAdmins.add(item);
}
}
}
}
/**
* Add a {@link DeviceAdminInfo} object to the internal collection of available admins for all
* active admin components associated with a profile.
*
* @param profileId a profile identifier.
*/
private void addActiveAdminsForProfile(final List<ComponentName> activeAdmins,
final int profileId) {
if (activeAdmins != null) {
final PackageManager packageManager = getActivity().getPackageManager();
final IPackageManager iPackageManager = AppGlobals.getPackageManager();
final int n = activeAdmins.size();
for (int i = 0; i < n; ++i) {
final ComponentName activeAdmin = activeAdmins.get(i);
final ActivityInfo ai;
try {
ai = iPackageManager.getReceiverInfo(activeAdmin,
PackageManager.GET_META_DATA |
PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS |
PackageManager.MATCH_DIRECT_BOOT_UNAWARE |
PackageManager.MATCH_DIRECT_BOOT_AWARE, profileId);
} catch (RemoteException e) {
Log.w(TAG, "Unable to load component: " + activeAdmin);
continue;
}
final DeviceAdminInfo deviceAdminInfo = createDeviceAdminInfo(ai);
if (deviceAdminInfo == null) {
continue;
}
// Don't do the applicationInfo.isInternal() check here; if an active
// admin is already on SD card, just show it.
final DeviceAdminListItem item = new DeviceAdminListItem();
item.info = deviceAdminInfo;
item.name = deviceAdminInfo.loadLabel(packageManager).toString();
item.active = true;
mAdmins.add(item);
}
}
}
/**
* Creates a device admin info object for the resolved intent that points to the component of
* the device admin.
*
* @param ai ActivityInfo for the admin component.
* @return new {@link DeviceAdminInfo} object or null if there was an error.
*/
private DeviceAdminInfo createDeviceAdminInfo(ActivityInfo ai) {
try {
return new DeviceAdminInfo(getActivity(), ai);
} catch (XmlPullParserException|IOException e) {
Log.w(TAG, "Skipping " + ai, e);
}
return null;
}
/**
* Extracts the user id from a device admin info object.
* @param adminInfo the device administrator info.
* @return identifier of the user associated with the device admin.
*/
private int getUserId(DeviceAdminInfo adminInfo) {
return UserHandle.getUserId(adminInfo.getActivityInfo().applicationInfo.uid);
} }
};
} }

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C) 2018 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.specialaccess.deviceadmin;
import android.app.admin.DeviceAdminInfo;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.os.UserHandle;
import android.util.Log;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
public class DeviceAdminUtils {
private static final String TAG = "DeviceAdminUtils";
/**
* Creates a device admin info object for the resolved intent that points to the component of
* the device admin.
*
* @param ai ActivityInfo for the admin component.
* @return new {@link DeviceAdminInfo} object or null if there was an error.
*/
public static DeviceAdminInfo createDeviceAdminInfo(Context context, ActivityInfo ai) {
try {
return new DeviceAdminInfo(context, ai);
} catch (XmlPullParserException | IOException e) {
Log.w(TAG, "Skipping " + ai, e);
}
return null;
}
/**
* Extracts the user id from a device admin info object.
*
* @param adminInfo the device administrator info.
* @return identifier of the user associated with the device admin.
*/
public static int getUserIdFromDeviceAdminInfo(DeviceAdminInfo adminInfo) {
return UserHandle.getUserId(adminInfo.getActivityInfo().applicationInfo.uid);
}
}

View File

@@ -1,36 +0,0 @@
/*
* 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.specialaccess.deviceadmin;
import android.content.Context;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
public class DeviceAdministratorsController extends BasePreferenceController {
public DeviceAdministratorsController(Context context, String key) {
super(context, key);
}
@AvailabilityStatus
public int getAvailabilityStatus() {
return mContext.getResources().getBoolean(R.bool.config_show_device_administrators)
? AVAILABLE
: UNSUPPORTED_ON_DEVICE;
}
}

View File

@@ -20,8 +20,6 @@ import android.app.Activity;
import android.app.AlarmManager; import android.app.AlarmManager;
import android.app.DatePickerDialog; import android.app.DatePickerDialog;
import android.content.Context; import android.content.Context;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.format.DateFormat; import android.text.format.DateFormat;
import android.widget.DatePicker; import android.widget.DatePicker;
@@ -32,6 +30,9 @@ import com.android.settingslib.core.AbstractPreferenceController;
import java.util.Calendar; import java.util.Calendar;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
public class DatePreferenceController extends AbstractPreferenceController public class DatePreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin, DatePickerDialog.OnDateSetListener { implements PreferenceControllerMixin, DatePickerDialog.OnDateSetListener {

View File

@@ -14,42 +14,38 @@
package com.android.settings.enterprise; package com.android.settings.enterprise;
import android.content.Context; import android.content.Context;
import androidx.preference.Preference;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.core.BasePreferenceController;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.AbstractPreferenceController;
public class ManageDeviceAdminPreferenceController extends AbstractPreferenceController implements import androidx.preference.Preference;
PreferenceControllerMixin {
public class ManageDeviceAdminPreferenceController extends BasePreferenceController {
private static final String KEY_MANAGE_DEVICE_ADMIN = "manage_device_admin";
private final EnterprisePrivacyFeatureProvider mFeatureProvider; private final EnterprisePrivacyFeatureProvider mFeatureProvider;
public ManageDeviceAdminPreferenceController(Context context) { public ManageDeviceAdminPreferenceController(Context context, String key) {
super(context); super(context, key);
mFeatureProvider = FeatureFactory.getFactory(context) mFeatureProvider = FeatureFactory.getFactory(context)
.getEnterprisePrivacyFeatureProvider(context); .getEnterprisePrivacyFeatureProvider(context);
} }
@Override @Override
public void updateState(Preference preference) { public CharSequence getSummary() {
final int activeAdmins final int activeAdmins
= mFeatureProvider.getNumberOfActiveDeviceAdminsForCurrentUserAndManagedProfile(); = mFeatureProvider.getNumberOfActiveDeviceAdminsForCurrentUserAndManagedProfile();
preference.setSummary(activeAdmins == 0 return activeAdmins == 0
? mContext.getResources().getString(R.string.number_of_device_admins_none) ? mContext.getResources().getString(R.string.number_of_device_admins_none)
: mContext.getResources().getQuantityString(R.plurals.number_of_device_admins, : mContext.getResources().getQuantityString(R.plurals.number_of_device_admins,
activeAdmins, activeAdmins)); activeAdmins, activeAdmins);
} }
@Override @Override
public boolean isAvailable() { public int getAvailabilityStatus() {
return mContext.getResources().getBoolean(R.bool.config_show_manage_device_admin); return mContext.getResources().getBoolean(R.bool.config_show_manage_device_admin)
? AVAILABLE_UNSEARCHABLE
: UNSUPPORTED_ON_DEVICE;
} }
@Override
public String getPreferenceKey() {
return KEY_MANAGE_DEVICE_ADMIN;
}
} }

View File

@@ -29,7 +29,6 @@ import com.android.settings.Utils;
import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.dashboard.SummaryLoader; import com.android.settings.dashboard.SummaryLoader;
import com.android.settings.enterprise.EnterprisePrivacyPreferenceController; import com.android.settings.enterprise.EnterprisePrivacyPreferenceController;
import com.android.settings.enterprise.ManageDeviceAdminPreferenceController;
import com.android.settings.fingerprint.FingerprintProfileStatusPreferenceController; import com.android.settings.fingerprint.FingerprintProfileStatusPreferenceController;
import com.android.settings.fingerprint.FingerprintStatusPreferenceController; import com.android.settings.fingerprint.FingerprintStatusPreferenceController;
import com.android.settings.location.LocationPreferenceController; import com.android.settings.location.LocationPreferenceController;
@@ -114,7 +113,6 @@ public class SecuritySettings extends DashboardFragment {
Lifecycle lifecycle, SecuritySettings host) { Lifecycle lifecycle, SecuritySettings host) {
final List<AbstractPreferenceController> controllers = new ArrayList<>(); final List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new LocationPreferenceController(context, lifecycle)); controllers.add(new LocationPreferenceController(context, lifecycle));
controllers.add(new ManageDeviceAdminPreferenceController(context));
controllers.add(new EnterprisePrivacyPreferenceController(context)); controllers.add(new EnterprisePrivacyPreferenceController(context));
controllers.add(new ManageTrustAgentsPreferenceController(context)); controllers.add(new ManageTrustAgentsPreferenceController(context));
controllers.add(new ScreenPinningPreferenceController(context)); controllers.add(new ScreenPinningPreferenceController(context));

View File

@@ -33,7 +33,6 @@
<bool name="config_show_notification_volume">false</bool> <bool name="config_show_notification_volume">false</bool>
<bool name="config_show_screen_locking_sounds">false</bool> <bool name="config_show_screen_locking_sounds">false</bool>
<bool name="config_show_touch_sounds">false</bool> <bool name="config_show_touch_sounds">false</bool>
<bool name="config_show_device_administrators">false</bool>
<bool name="config_show_encryption_and_credentials_encryption_status">false</bool> <bool name="config_show_encryption_and_credentials_encryption_status">false</bool>
<bool name="config_show_premium_sms">false</bool> <bool name="config_show_premium_sms">false</bool>
<bool name="config_show_data_saver">false</bool> <bool name="config_show_data_saver">false</bool>

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2018 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.specialaccess.deviceadmin;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import android.app.admin.DeviceAdminInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
@RunWith(SettingsRobolectricTestRunner.class)
public class DeviceAdminListItemTest {
@Mock
private DeviceAdminInfo mDeviceAdminInfo;
private Context mContext;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
}
@Test
public void newInstance_shouldLoadInfoFromDeviceAdminInfo() {
final String label = "testlabel";
final String description = "testdesc";
final ComponentName cn = new ComponentName(mContext.getPackageName(), "test");
when(mDeviceAdminInfo.getActivityInfo()).thenReturn(new ActivityInfo());
mDeviceAdminInfo.getActivityInfo().applicationInfo = new ApplicationInfo();
when(mDeviceAdminInfo.loadLabel(any(PackageManager.class))).thenReturn(label);
when(mDeviceAdminInfo.loadDescription(any(PackageManager.class))).thenReturn(description);
when(mDeviceAdminInfo.loadIcon(any(PackageManager.class)))
.thenReturn(new ColorDrawable(Color.BLUE));
when(mDeviceAdminInfo.getComponent()).thenReturn(cn);
DeviceAdminListItem item = new DeviceAdminListItem(mContext, mDeviceAdminInfo);
assertThat(item.getKey()).isEqualTo(cn.flattenToShortString());
assertThat(item.getName()).isEqualTo(label);
assertThat(item.getDescription()).isEqualTo(description);
}
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright (C) 2018 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.specialaccess.deviceadmin;
import static androidx.lifecycle.Lifecycle.Event.ON_START;
import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.IntentFilter;
import android.os.UserHandle;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.widget.FooterPreferenceMixin;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import androidx.lifecycle.LifecycleOwner;
@RunWith(SettingsRobolectricTestRunner.class)
public class DeviceAdminListPreferenceControllerTest {
@Mock
private FooterPreferenceMixin mFooterPreferenceMixin;
private Context mContext;
private DeviceAdminListPreferenceController mController;
private LifecycleOwner mLifecycleOwner;
private Lifecycle mLifecycle;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
mController = spy(new DeviceAdminListPreferenceController(mContext, "test_key")
.setFooterPreferenceMixin(mFooterPreferenceMixin));
mLifecycle.addObserver(mController);
}
@Test
public void onStart_registerReceiver() {
mLifecycle.handleLifecycleEvent(ON_START);
verify(mContext).registerReceiverAsUser(any(BroadcastReceiver.class), eq(UserHandle.ALL),
any(IntentFilter.class), isNull(), isNull());
}
@Test
public void onStop_unregisterReceiver() {
mLifecycle.handleLifecycleEvent(ON_START);
mLifecycle.handleLifecycleEvent(ON_STOP);
verify(mContext).unregisterReceiver(any(BroadcastReceiver.class));
}
}

View File

@@ -1,56 +0,0 @@
/*
* 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.specialaccess.deviceadmin;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
import android.content.Context;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
@RunWith(SettingsRobolectricTestRunner.class)
public class DeviceAdministratorsControllerTest {
private Context mContext;
private DeviceAdministratorsController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application.getApplicationContext());
mController = new DeviceAdministratorsController(mContext, "key");
}
@Test
public void testDeviceAdministrators_byDefault_shouldBeShown() {
assertThat(mController.isAvailable()).isTrue();
}
@Test
@Config(qualifiers = "mcc999")
public void testDeviceAdministrators_ifDisabled_shouldNotBeShown() {
assertThat(mController.isAvailable()).isFalse();
}
}

View File

@@ -22,7 +22,6 @@ import static org.mockito.Mockito.when;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import androidx.preference.Preference;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.FakeFeatureFactory;
@@ -36,6 +35,8 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import androidx.preference.Preference;
@RunWith(SettingsRobolectricTestRunner.class) @RunWith(SettingsRobolectricTestRunner.class)
public class ManageDeviceAdminPreferenceControllerTest { public class ManageDeviceAdminPreferenceControllerTest {
@@ -51,7 +52,7 @@ public class ManageDeviceAdminPreferenceControllerTest {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application); mContext = spy(RuntimeEnvironment.application);
mFeatureFactory = FakeFeatureFactory.setupForTest(); mFeatureFactory = FakeFeatureFactory.setupForTest();
mController = new ManageDeviceAdminPreferenceController(mContext); mController = new ManageDeviceAdminPreferenceController(mContext, "testkey");
} }
@Test @Test
@@ -60,7 +61,7 @@ public class ManageDeviceAdminPreferenceControllerTest {
when(mFeatureFactory.enterprisePrivacyFeatureProvider when(mFeatureFactory.enterprisePrivacyFeatureProvider
.getNumberOfActiveDeviceAdminsForCurrentUserAndManagedProfile()).thenReturn(0); .getNumberOfActiveDeviceAdminsForCurrentUserAndManagedProfile()).thenReturn(0);
when (mContext.getResources()).thenReturn(mResources); when(mContext.getResources()).thenReturn(mResources);
when(mResources.getString(R.string.number_of_device_admins_none)) when(mResources.getString(R.string.number_of_device_admins_none))
.thenReturn("no apps"); .thenReturn("no apps");
mController.updateState(preference); mController.updateState(preference);
@@ -84,15 +85,4 @@ public class ManageDeviceAdminPreferenceControllerTest {
public void isAvailable_whenNotVisible_isFalse() { public void isAvailable_whenNotVisible_isFalse() {
assertThat(mController.isAvailable()).isFalse(); assertThat(mController.isAvailable()).isFalse();
} }
@Test
public void testHandlePreferenceTreeClick() {
assertThat(mController.handlePreferenceTreeClick(new Preference(mContext, null, 0, 0)))
.isFalse();
}
@Test
public void testGetPreferenceKey() {
assertThat(mController.getPreferenceKey()).isEqualTo("manage_device_admin");
}
} }