diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 554601a3074e2..30154c8f4a73e 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -859,7 +859,11 @@ package android.app { public class StatusBarManager { method @NonNull @RequiresPermission(android.Manifest.permission.STATUS_BAR) public android.app.StatusBarManager.DisableInfo getDisableInfo(); + method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public int getNavBarModeOverride(); method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSetup(boolean); + method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setNavBarModeOverride(int); + field public static final int NAV_BAR_MODE_OVERRIDE_KIDS = 1; // 0x1 + field public static final int NAV_BAR_MODE_OVERRIDE_NONE = 0; // 0x0 } public static final class StatusBarManager.DisableInfo { diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index ae578f5e60ed0..64d3a9f08548e 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -282,6 +282,33 @@ public class StatusBarManager { @Retention(RetentionPolicy.SOURCE) public @interface RequestResult {} + /** + * Constant for {@link #setNavBarModeOverride(int)} indicating the default navbar mode. + * + * @hide + */ + @SystemApi + public static final int NAV_BAR_MODE_OVERRIDE_NONE = 0; + + /** + * Constant for {@link #setNavBarModeOverride(int)} indicating kids navbar mode. + * + *

When used, back and home icons will change drawables and layout, recents will be hidden, + * and the navbar will remain visible when apps are in immersive mode. + * + * @hide + */ + @SystemApi + public static final int NAV_BAR_MODE_OVERRIDE_KIDS = 1; + + /** @hide */ + @IntDef(prefix = {"NAV_BAR_MODE_OVERRIDE_"}, value = { + NAV_BAR_MODE_OVERRIDE_NONE, + NAV_BAR_MODE_OVERRIDE_KIDS + }) + @Retention(RetentionPolicy.SOURCE) + public @interface NavBarModeOverride {} + @UnsupportedAppUsage private Context mContext; private IStatusBarService mService; @@ -687,6 +714,52 @@ public class StatusBarManager { } } + /** + * Sets or removes the navigation bar mode override. + * + * @param navBarModeOverride the mode of the navigation bar override to be set. + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.STATUS_BAR) + public void setNavBarModeOverride(@NavBarModeOverride int navBarModeOverride) { + if (navBarModeOverride != NAV_BAR_MODE_OVERRIDE_NONE + && navBarModeOverride != NAV_BAR_MODE_OVERRIDE_KIDS) { + throw new UnsupportedOperationException( + "Supplied navBarModeOverride not supported: " + navBarModeOverride); + } + + try { + final IStatusBarService svc = getService(); + if (svc != null) { + svc.setNavBarModeOverride(navBarModeOverride); + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Gets the navigation bar mode override. Returns default value if no override is set. + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.STATUS_BAR) + public @NavBarModeOverride int getNavBarModeOverride() { + int navBarModeOverride = NAV_BAR_MODE_OVERRIDE_NONE; + try { + final IStatusBarService svc = getService(); + if (svc != null) { + navBarModeOverride = svc.getNavBarModeOverride(); + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + return navBarModeOverride; + } + /** @hide */ public static String windowStateToString(int state) { if (state == WINDOW_STATE_HIDING) return "WINDOW_STATE_HIDING"; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 6c045442fe4a8..ea9540b558803 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -10118,6 +10118,14 @@ public final class Settings { public static final String THEME_CUSTOMIZATION_OVERLAY_PACKAGES = "theme_customization_overlay_packages"; + /** + * Indicates whether the device is in kids nav mode. + *

Type: int (0 for false, 1 for true) + * + * @hide + */ + public static final String NAV_BAR_KIDS_MODE = "nav_bar_kids_mode"; + /** * Navigation bar mode. * 0 = 3 button diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index 1db7426ba6cff..3c6b7ff60a030 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -162,4 +162,20 @@ interface IStatusBarService void requestAddTile(in ComponentName componentName, in CharSequence label, in Icon icon, int userId, in IAddTileResultCallback callback); void cancelRequestAddTile(in String packageName); + + /** + * Overrides the navigation bar mode. + * + * @param navBarModeOverride the mode of the navigation bar override to be set. + * + * @hide + */ + void setNavBarModeOverride(int navBarModeOverride); + + /** + * Gets the navigation bar mode override. + * + * @hide + */ + int getNavBarModeOverride(); } diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto index 5090d8cb5c3af..00b7fd7f08872 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -383,6 +383,7 @@ message SecureSettingsProto { optional SettingProto multi_press_timeout = 38 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto nav_bar_kids_mode = 91 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto navigation_mode = 76 [ (android.privacy).dest = DEST_AUTOMATIC ]; message NfcPayment { @@ -665,5 +666,5 @@ message SecureSettingsProto { // Please insert fields in alphabetical order and group them into messages // if possible (to avoid reaching the method limit). - // Next tag = 91; + // Next tag = 92; } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index 688c48ddebb1b..13c1e512cb144 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -229,6 +229,7 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.SKIP_DIRECTION, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.SILENCE_GESTURE, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES, JSON_OBJECT_VALIDATOR); + VALIDATORS.put(Secure.NAV_BAR_KIDS_MODE, BOOLEAN_VALIDATOR); VALIDATORS.put( Secure.NAVIGATION_MODE, new DiscreteValueValidator(new String[] {"0", "1", "2"})); VALIDATORS.put(Secure.BACK_GESTURE_INSET_SCALE_LEFT, diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index d4f9f91b6db45..cd6447fae8cde 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -2243,6 +2243,10 @@ class SettingsProtoDumpUtil { Settings.Secure.MULTI_PRESS_TIMEOUT, SecureSettingsProto.MULTI_PRESS_TIMEOUT); + dumpSetting(s, p, + Settings.Secure.NAV_BAR_KIDS_MODE, + SecureSettingsProto.NAV_BAR_KIDS_MODE); + dumpSetting(s, p, Settings.Secure.NAVIGATION_MODE, SecureSettingsProto.NAVIGATION_MODE); diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 4eae939f9e34c..54ec8845b24fb 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -18,6 +18,9 @@ package com.android.server.statusbar; import static android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS; import static android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE; +import static android.app.StatusBarManager.NAV_BAR_MODE_OVERRIDE_KIDS; +import static android.app.StatusBarManager.NAV_BAR_MODE_OVERRIDE_NONE; +import static android.app.StatusBarManager.NavBarModeOverride; import static android.view.Display.DEFAULT_DISPLAY; import android.Manifest; @@ -59,6 +62,7 @@ import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.UserHandle; +import android.provider.Settings; import android.service.notification.NotificationStats; import android.service.quicksettings.TileService; import android.text.TextUtils; @@ -1846,6 +1850,51 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D return mContext.getResources().getStringArray(R.array.config_statusBarIcons); } + /** + * Sets or removes the navigation bar mode override. + * + * @param navBarModeOverride the mode of the navigation bar override to be set. + */ + public void setNavBarModeOverride(@NavBarModeOverride int navBarModeOverride) { + enforceStatusBar(); + if (navBarModeOverride != NAV_BAR_MODE_OVERRIDE_NONE + && navBarModeOverride != NAV_BAR_MODE_OVERRIDE_KIDS) { + throw new UnsupportedOperationException( + "Supplied navBarModeOverride not supported: " + navBarModeOverride); + } + + final int userId = mCurrentUserId; + final long userIdentity = Binder.clearCallingIdentity(); + try { + Settings.Secure.putIntForUser(mContext.getContentResolver(), + Settings.Secure.NAV_BAR_KIDS_MODE, navBarModeOverride, userId); + } finally { + Binder.restoreCallingIdentity(userIdentity); + } + } + + /** + * Gets the navigation bar mode override. Returns default value if no override is set. + * + * @hide + */ + public @NavBarModeOverride int getNavBarModeOverride() { + enforceStatusBar(); + + int navBarKidsMode = NAV_BAR_MODE_OVERRIDE_NONE; + final int userId = mCurrentUserId; + final long userIdentity = Binder.clearCallingIdentity(); + try { + navBarKidsMode = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.NAV_BAR_KIDS_MODE, userId); + } catch (Settings.SettingNotFoundException ex) { + return navBarKidsMode; + } finally { + Binder.restoreCallingIdentity(userIdentity); + } + return navBarKidsMode; + } + /** @hide */ public void passThroughShellCommand(String[] args, FileDescriptor fd) { enforceStatusBarOrShell(); diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index e756124e2e99e..202a54d36cc91 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -57,6 +57,7 @@ + diff --git a/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java index c293b5e7c5930..d164d2a4f5818 100644 --- a/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java @@ -21,6 +21,7 @@ import static android.app.ActivityManager.PROCESS_STATE_TOP; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.any; @@ -575,6 +576,30 @@ public class StatusBarManagerServiceTest { } } + @Test + public void testSetNavBarModeOverride_setsOverrideModeKids() { + int navBarModeOverrideKids = StatusBarManager.NAV_BAR_MODE_OVERRIDE_KIDS; + mStatusBarManagerService.setNavBarModeOverride(navBarModeOverrideKids); + + assertEquals(navBarModeOverrideKids, mStatusBarManagerService.getNavBarModeOverride()); + } + + @Test + public void testSetNavBarModeOverride_setsOverrideModeNone() { + int navBarModeOverrideNone = StatusBarManager.NAV_BAR_MODE_OVERRIDE_NONE; + mStatusBarManagerService.setNavBarModeOverride(navBarModeOverrideNone); + + assertEquals(navBarModeOverrideNone, mStatusBarManagerService.getNavBarModeOverride()); + } + + @Test + public void testSetNavBarModeOverride_invalidInputThrowsError() { + int navBarModeOverrideInvalid = -1; + + assertThrows(UnsupportedOperationException.class, + () -> mStatusBarManagerService.setNavBarModeOverride(navBarModeOverrideInvalid)); + } + private void mockUidCheck() { mockUidCheck(TEST_PACKAGE); }