From 56d145fef9ce232c5f9bbe739e8def9ade84c86a Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 1 Jul 2019 12:47:57 -0700 Subject: [PATCH] Autoenabling gesture mode only is the device is unmanaged > Adding a protected broadcast to enable gesture mode If the device is not managed, gesture mode will be wnabled once setup-wizard finishes. Otherwise, it will wait for a protected broadcast from the device/profile owner app which can switch the device to gesture mode. Bug: 134473386 Test: Verified on device setup in device owner and profile owner mode Change-Id: I7d32d5461eef19a30f323295c7a9bac975adbf91 --- .../shared/system/QuickStepContract.java | 9 ++ .../phone/NavigationModeController.java | 123 +++++++++++++++++- 2 files changed, 131 insertions(+), 1 deletion(-) diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index 7fbe5db99569e..cc7863c0113b6 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -49,6 +49,15 @@ public class QuickStepContract { public static final String NAV_BAR_MODE_GESTURAL_OVERLAY = WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY; + // Action sent by a system app to switch to gesture nav + public static final String ACTION_ENABLE_GESTURE_NAV = + "com.android.systemui.ENABLE_GESTURE_NAV"; + // Action for the intent to receive the result + public static final String ACTION_ENABLE_GESTURE_NAV_RESULT = + "com.android.systemui.action.ENABLE_GESTURE_NAV_RESULT"; + // Extra containing the pending intent to receive the result + public static final String EXTRA_RESULT_INTENT = "com.android.systemui.EXTRA_RESULT_INTENT"; + // Overview is disabled, either because the device is in lock task mode, or because the device // policy has disabled the feature public static final int SYSUI_STATE_SCREEN_PINNING = 1 << 0; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java index 4d7cf2715f9c0..64fef55a21d3a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java @@ -16,17 +16,26 @@ package com.android.systemui.statusbar.phone; +import static android.app.Activity.RESULT_CANCELED; +import static android.app.Activity.RESULT_OK; +import static android.app.admin.DevicePolicyManager.STATE_USER_UNMANAGED; import static android.content.Intent.ACTION_OVERLAY_CHANGED; import static android.content.Intent.ACTION_PREFERRED_ACTIVITY_CHANGED; +import static android.content.pm.PackageManager.FEATURE_DEVICE_ADMIN; import static android.os.UserHandle.USER_CURRENT; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY; +import static com.android.systemui.shared.system.QuickStepContract.ACTION_ENABLE_GESTURE_NAV; +import static com.android.systemui.shared.system.QuickStepContract.ACTION_ENABLE_GESTURE_NAV_RESULT; +import static com.android.systemui.shared.system.QuickStepContract.EXTRA_RESULT_INTENT; + import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; +import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -40,6 +49,7 @@ import android.os.PatternMatcher; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; +import android.os.UserManager; import android.provider.Settings; import android.provider.Settings.Secure; import android.text.TextUtils; @@ -157,6 +167,8 @@ public class NavigationModeController implements Dumpable { } }; + private BroadcastReceiver mEnableGestureNavReceiver; + @Inject public NavigationModeController(Context context, DeviceProvisionedController deviceProvisionedController, @@ -177,6 +189,7 @@ public class NavigationModeController implements Dumpable { IntentFilter preferredActivityFilter = new IntentFilter(ACTION_PREFERRED_ACTIVITY_CHANGED); mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, preferredActivityFilter, null, null); + // We are only interested in launcher changes, so keeping track of the current default. mLastDefaultLauncher = getDefaultLauncherPackageName(mContext); @@ -187,6 +200,82 @@ public class NavigationModeController implements Dumpable { deferGesturalNavOverlayIfNecessary(); } + private void removeEnableGestureNavListener() { + if (mEnableGestureNavReceiver != null) { + if (DEBUG) { + Log.d(TAG, "mEnableGestureNavReceiver unregistered"); + } + mContext.unregisterReceiver(mEnableGestureNavReceiver); + mEnableGestureNavReceiver = null; + } + } + + private boolean setGestureModeOverlayForMainLauncher() { + removeEnableGestureNavListener(); + if (getCurrentInteractionMode(mCurrentUserContext) == NAV_BAR_MODE_GESTURAL) { + // Already in gesture mode + return true; + } + final Boolean supported = isGestureNavSupportedByDefaultLauncher(mCurrentUserContext); + if (supported == null || supported) { + Log.d(TAG, "Switching system navigation to full-gesture mode:" + + " defaultLauncher=" + + getDefaultLauncherPackageName(mCurrentUserContext) + + " contextUser=" + + mCurrentUserContext.getUserId()); + + setModeOverlay(NAV_BAR_MODE_GESTURAL_OVERLAY, USER_CURRENT); + return true; + } else { + Log.e(TAG, "Gesture nav is not supported for defaultLauncher=" + + getDefaultLauncherPackageName(mCurrentUserContext)); + return false; + } + } + + private boolean enableGestureNav(Intent intent) { + if (!(intent.getParcelableExtra(EXTRA_RESULT_INTENT) instanceof PendingIntent)) { + Log.e(TAG, "No callback pending intent was attached"); + return false; + } + + PendingIntent callback = intent.getParcelableExtra(EXTRA_RESULT_INTENT); + Intent callbackIntent = callback.getIntent(); + if (callbackIntent == null + || !ACTION_ENABLE_GESTURE_NAV_RESULT.equals(callbackIntent.getAction())) { + Log.e(TAG, "Invalid callback intent"); + return false; + } + String callerPackage = callback.getCreatorPackage(); + UserHandle callerUser = callback.getCreatorUserHandle(); + + DevicePolicyManager dpm = mCurrentUserContext.getSystemService(DevicePolicyManager.class); + ComponentName ownerComponent = dpm.getDeviceOwnerComponentOnCallingUser(); + + if (ownerComponent != null) { + // Verify that the caller is the owner component + if (!ownerComponent.getPackageName().equals(callerPackage) + || !mCurrentUserContext.getUser().equals(callerUser)) { + Log.e(TAG, "Callback must be from the device owner"); + return false; + } + } else { + UserHandle callerParent = mCurrentUserContext.getSystemService(UserManager.class) + .getProfileParent(callerUser); + if (callerParent == null || !callerParent.equals(mCurrentUserContext.getUser())) { + Log.e(TAG, "Callback must be from a managed user"); + return false; + } + ComponentName profileOwner = dpm.getProfileOwnerAsUser(callerUser); + if (profileOwner == null || !profileOwner.getPackageName().equals(callerPackage)) { + Log.e(TAG, "Callback must be from the profile owner"); + return false; + } + } + + return setGestureModeOverlayForMainLauncher(); + } + public void updateCurrentInteractionMode(boolean notify) { mCurrentUserContext = getCurrentUserContext(); int mode = getCurrentInteractionMode(mCurrentUserContext); @@ -245,6 +334,10 @@ public class NavigationModeController implements Dumpable { } } + private boolean supportsDeviceAdmin() { + return mContext.getPackageManager().hasSystemFeature(FEATURE_DEVICE_ADMIN); + } + private void deferGesturalNavOverlayIfNecessary() { final int userId = mDeviceProvisionedController.getCurrentUser(); mRestoreGesturalNavBarMode.put(userId, false); @@ -255,6 +348,7 @@ public class NavigationModeController implements Dumpable { Log.d(TAG, "deferGesturalNavOverlayIfNecessary: device is provisioned and user is " + "setup"); } + removeEnableGestureNavListener(); return; } @@ -270,6 +364,7 @@ public class NavigationModeController implements Dumpable { Log.d(TAG, "deferGesturalNavOverlayIfNecessary: no default gestural overlay, " + "default=" + defaultOverlays); } + removeEnableGestureNavListener(); return; } @@ -277,6 +372,24 @@ public class NavigationModeController implements Dumpable { // provisioned setModeOverlay(NAV_BAR_MODE_3BUTTON_OVERLAY, USER_CURRENT); mRestoreGesturalNavBarMode.put(userId, true); + + if (supportsDeviceAdmin() && mEnableGestureNavReceiver == null) { + mEnableGestureNavReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (DEBUG) { + Log.d(TAG, "ACTION_ENABLE_GESTURE_NAV"); + } + setResultCode(enableGestureNav(intent) ? RESULT_OK : RESULT_CANCELED); + } + }; + // Register for all users so that we can get managed users as well + mContext.registerReceiverAsUser(mEnableGestureNavReceiver, UserHandle.ALL, + new IntentFilter(ACTION_ENABLE_GESTURE_NAV), null, null); + if (DEBUG) { + Log.d(TAG, "mEnableGestureNavReceiver registered"); + } + } if (DEBUG) { Log.d(TAG, "deferGesturalNavOverlayIfNecessary: setting to 3 button mode"); } @@ -290,7 +403,15 @@ public class NavigationModeController implements Dumpable { final int userId = mDeviceProvisionedController.getCurrentUser(); if (mRestoreGesturalNavBarMode.get(userId)) { // Restore the gestural state if necessary - setModeOverlay(NAV_BAR_MODE_GESTURAL_OVERLAY, USER_CURRENT); + if (!supportsDeviceAdmin() + || mCurrentUserContext.getSystemService(DevicePolicyManager.class) + .getUserProvisioningState() == STATE_USER_UNMANAGED) { + setGestureModeOverlayForMainLauncher(); + } else { + if (DEBUG) { + Log.d(TAG, "Not restoring to gesture nav for managed user"); + } + } mRestoreGesturalNavBarMode.put(userId, false); } }