summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Alex Chau <alexchau@google.com> 2017-11-27 18:21:23 +0000
committer Alex Chau <alexchau@google.com> 2017-12-04 21:42:44 +0000
commit044588599c97ceff70d74a133c9eb01a028db00c (patch)
tree9d95d28f2be0a80b3dfb13e38278fb9f87f1ce03
parent5826e469e9d03eaa5511d413733edde0a1b99048 (diff)
Introduce logout button and DevicePolicyManager API to enable it
Bug: 67843538 Test: Manual test with CloudDpc that logout button can be controlled by DevicePolicyManager API and only enabled in shared userse Test: Logout button can actually exit the user and ActivityManager log shows that user is stopped Test: CTS is tracked in b/67843605 Change-Id: I9f27050654958ce55f574dd05ff80609255ffeb4
-rw-r--r--api/current.txt2
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java32
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl3
-rw-r--r--core/res/res/drawable/ic_logout.xml15
-rw-r--r--core/res/res/values/config.xml2
-rw-r--r--core/res/res/values/strings.xml3
-rw-r--r--core/res/res/values/symbols.xml3
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java49
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java43
9 files changed, 151 insertions, 1 deletions
diff --git a/api/current.txt b/api/current.txt
index 279a6f2f69b8..7a091ee3e71f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6388,6 +6388,7 @@ package android.app.admin {
method public deprecated boolean isCallerApplicationRestrictionsManagingPackage();
method public boolean isDeviceOwnerApp(java.lang.String);
method public boolean isLockTaskPermitted(java.lang.String);
+ method public boolean isLogoutButtonEnabled();
method public boolean isManagedProfile(android.content.ComponentName);
method public boolean isMasterVolumeMuted(android.content.ComponentName);
method public boolean isNetworkLoggingEnabled(android.content.ComponentName);
@@ -6430,6 +6431,7 @@ package android.app.admin {
method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
method public void setLockTaskFeatures(android.content.ComponentName, int);
method public void setLockTaskPackages(android.content.ComponentName, java.lang.String[]) throws java.lang.SecurityException;
+ method public void setLogoutButtonEnabled(android.content.ComponentName, boolean);
method public void setLongSupportMessage(android.content.ComponentName, java.lang.CharSequence);
method public void setMasterVolumeMuted(android.content.ComponentName, boolean);
method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 0bca96907988..6b2d904eee0e 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -8444,6 +8444,38 @@ public class DevicePolicyManager {
}
/**
+ * Called by a device owner to specify whether a logout button is enabled for all secondary
+ * users. The system may show a logout button that stops the user and switches back to the
+ * primary user.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param enabled whether logout button should be enabled or not.
+ * @throws SecurityException if {@code admin} is not a device owner.
+ */
+ public void setLogoutButtonEnabled(@NonNull ComponentName admin, boolean enabled) {
+ throwIfParentInstance("setLogoutButtonEnabled");
+ try {
+ mService.setLogoutButtonEnabled(admin, enabled);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns whether logout button is enabled by a device owner.
+ *
+ * @return {@code true} if logout button is enabled by device owner, {@code false} otherwise.
+ */
+ public boolean isLogoutButtonEnabled() {
+ throwIfParentInstance("isLogoutButtonEnabled");
+ try {
+ return mService.isLogoutButtonEnabled();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Callback used in {@link #clearApplicationUserData}
* to indicate that the clearing of an application's user data is done.
*/
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index b7740e9e7314..8078d38fa6dd 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -365,4 +365,7 @@ interface IDevicePolicyManager {
StringParceledListSlice getOwnerInstalledCaCerts(in UserHandle user);
boolean clearApplicationUserData(in ComponentName admin, in String packageName, in IPackageDataObserver callback);
+
+ void setLogoutButtonEnabled(in ComponentName admin, boolean enabled);
+ boolean isLogoutButtonEnabled();
}
diff --git a/core/res/res/drawable/ic_logout.xml b/core/res/res/drawable/ic_logout.xml
new file mode 100644
index 000000000000..c016ec818e4f
--- /dev/null
+++ b/core/res/res/drawable/ic_logout.xml
@@ -0,0 +1,15 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <group>
+ <clip-path android:pathData="M0,0h24v24H0V0z M 0,0" />
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17.0,7.0l-1.4099998,1.4099998l2.58,2.5900002l-10.17,0.0l0.0,2.0l10.17,0.0l-2.58,2.58l1.4099998,1.4200001l5.0,-5.0z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4,5h8V3H4C2.9,3,2,3.9,2,5v14c0,1.1,0.9,2,2,2h8v-2H4V5z"/>
+ </group>
+</vector>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fc649188fff6..0b38d1b1fca1 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2441,10 +2441,12 @@
"users" = list of users
"restart" = restart device
"lockdown" = Lock down device until the user authenticates
+ "logout" = Logout the current user
-->
<string-array translatable="false" name="config_globalActionsList">
<item>power</item>
<item>restart</item>
+ <item>logout</item>
<item>bugreport</item>
<item>users</item>
<item>lockdown</item>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 93bd4ec3bcf7..8958af4bffc8 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -471,6 +471,9 @@
<!-- label for item that generates a bug report in the phone options dialog -->
<string name="global_action_bug_report">Bug report</string>
+ <!-- label for item that logouts the current user -->
+ <string name="global_action_logout">End session</string>
+
<!-- Take bug report menu title [CHAR LIMIT=NONE] -->
<string name="bugreport_title">Take bug report</string>
<!-- Message in bugreport dialog describing what it does [CHAR LIMIT=NONE] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d944e4bce579..e0f7435869c8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3163,4 +3163,7 @@
<java-symbol type="string" name="unsupported_compile_sdk_check_update" />
<java-symbol type="string" name="battery_saver_warning_title" />
+
+ <java-symbol type="string" name="global_action_logout" />
+ <java-symbol type="drawable" name="ic_logout" />
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 00e8b1a44e47..3b54e11447b7 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -19,6 +19,7 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR
import android.app.ActivityManager;
import android.app.Dialog;
import android.app.WallpaperManager;
+import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
@@ -112,11 +113,13 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private static final String GLOBAL_ACTION_KEY_VOICEASSIST = "voiceassist";
private static final String GLOBAL_ACTION_KEY_ASSIST = "assist";
private static final String GLOBAL_ACTION_KEY_RESTART = "restart";
+ private static final String GLOBAL_ACTION_KEY_LOGOUT = "logout";
private final Context mContext;
private final GlobalActionsManager mWindowManagerFuncs;
private final AudioManager mAudioManager;
private final IDreamManager mDreamManager;
+ private final DevicePolicyManager mDevicePolicyManager;
private ArrayList<Action> mItems;
private ActionsDialog mDialog;
@@ -132,6 +135,7 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private boolean mIsWaitingForEcmExit = false;
private boolean mHasTelephony;
private boolean mHasVibrator;
+ private boolean mHasLogoutButton;
private final boolean mShowSilentToggle;
private final EmergencyAffordanceManager mEmergencyAffordanceManager;
@@ -144,6 +148,8 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
mDreamManager = IDreamManager.Stub.asInterface(
ServiceManager.getService(DreamService.DREAM_SERVICE));
+ mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
+ Context.DEVICE_POLICY_SERVICE);
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -289,6 +295,7 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener,
R.array.config_globalActionsList);
ArraySet<String> addedKeys = new ArraySet<String>();
+ mHasLogoutButton = false;
for (int i = 0; i < defaultActions.length; i++) {
String actionKey = defaultActions[i];
if (addedKeys.contains(actionKey)) {
@@ -325,6 +332,12 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mItems.add(getAssistAction());
} else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) {
mItems.add(new RestartAction());
+ } else if (GLOBAL_ACTION_KEY_LOGOUT.equals(actionKey)) {
+ if (mDevicePolicyManager.isLogoutButtonEnabled()
+ && getCurrentUser().id != UserHandle.USER_SYSTEM) {
+ mItems.add(new LogoutAction());
+ mHasLogoutButton = true;
+ }
} else {
Log.e(TAG, "Invalid global action key " + actionKey);
}
@@ -490,6 +503,37 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
}
+ private final class LogoutAction extends SinglePressAction {
+ private LogoutAction() {
+ super(R.drawable.ic_logout, R.string.global_action_logout);
+ }
+
+ @Override
+ public boolean showDuringKeyguard() {
+ return true;
+ }
+
+ @Override
+ public boolean showBeforeProvisioning() {
+ return false;
+ }
+
+ @Override
+ public void onPress() {
+ // Add a little delay before executing, to give the dialog a chance to go away before
+ // switching user
+ mHandler.postDelayed(() -> {
+ try {
+ ActivityManager.getService().switchUser(UserHandle.USER_SYSTEM);
+ ActivityManager.getService().stopUser(getCurrentUser().id, true /*force*/,
+ null);
+ } catch (RemoteException re) {
+ Log.e(TAG, "Couldn't logout user " + re);
+ }
+ }, 500);
+ }
+ }
+
private Action getSettingsAction() {
return new SinglePressAction(R.drawable.ic_settings,
R.string.global_action_settings) {
@@ -764,7 +808,10 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener,
public View getView(int position, View convertView, ViewGroup parent) {
Action action = getItem(position);
View view = action.create(mContext, convertView, parent, LayoutInflater.from(mContext));
- if (position == 2) {
+ // When there is no logout button, only power off and restart should be in white
+ // background, thus setting division view at third item; with logout button being the
+ // third item, set the division view at fourth item instead.
+ if (position == (mHasLogoutButton ? 3 : 2)) {
HardwareUiLayout.get(parent).setDivisionView(view);
}
return view;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index d7e4a6229bd4..b90b1cf47367 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -730,6 +730,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private static final String TAG_ORGANIZATION_NAME = "organization-name";
private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification";
private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
+ private static final String TAG_IS_LOGOUT_BUTTON_ENABLED = "is_logout_button_enabled";
final DeviceAdminInfo info;
@@ -779,6 +780,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
boolean requireAutoTime = false; // Can only be set by a device owner.
boolean forceEphemeralUsers = false; // Can only be set by a device owner.
boolean isNetworkLoggingEnabled = false; // Can only be set by a device owner.
+ boolean isLogoutButtonEnabled = false; // Can only be set by a device owner.
// one notification after enabling + one more after reboots
static final int DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN = 2;
@@ -1096,6 +1098,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
out.text(organizationName);
out.endTag(null, TAG_ORGANIZATION_NAME);
}
+ if (isLogoutButtonEnabled) {
+ out.startTag(null, TAG_IS_LOGOUT_BUTTON_ENABLED);
+ out.attribute(null, ATTR_VALUE, Boolean.toString(isLogoutButtonEnabled));
+ out.endTag(null, TAG_IS_LOGOUT_BUTTON_ENABLED);
+ }
}
void writePackageListToXml(XmlSerializer out, String outerTag,
@@ -1269,6 +1276,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
} else {
Log.w(LOG_TAG, "Missing text when loading organization name");
}
+ } else if (TAG_IS_LOGOUT_BUTTON_ENABLED.equals(tag)) {
+ isLogoutButtonEnabled = Boolean.parseBoolean(
+ parser.getAttributeValue(null, ATTR_VALUE));
} else {
Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
XmlUtils.skipCurrentTag(parser);
@@ -11405,4 +11415,37 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
return false;
}
+
+ @Override
+ public synchronized void setLogoutButtonEnabled(ComponentName admin, boolean enabled) {
+ if (!mHasFeature) {
+ return;
+ }
+ Preconditions.checkNotNull(admin);
+ getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+
+ if (enabled == isLogoutButtonEnabledInternalLocked()) {
+ // already in the requested state
+ return;
+ }
+ ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+ deviceOwner.isLogoutButtonEnabled = enabled;
+ saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+ }
+
+ @Override
+ public boolean isLogoutButtonEnabled() {
+ if (!mHasFeature) {
+ return false;
+ }
+ synchronized (this) {
+ return isLogoutButtonEnabledInternalLocked();
+ }
+ }
+
+ private boolean isLogoutButtonEnabledInternalLocked() {
+ ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+ return (deviceOwner != null) && deviceOwner.isLogoutButtonEnabled;
+ }
+
}