diff --git a/api/system-current.txt b/api/system-current.txt index 55b32f6c3b936..39c6fbdb57771 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -6204,6 +6204,10 @@ package android.app { method public void onDetached(); } + public class VrManager { + method public void setPersistentVrModeEnabled(boolean); + } + public final class WallpaperInfo implements android.os.Parcelable { ctor public WallpaperInfo(android.content.Context, android.content.pm.ResolveInfo) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; method public int describeContents(); @@ -9006,6 +9010,7 @@ package android.content { field public static final java.lang.String USB_SERVICE = "usb"; field public static final java.lang.String USER_SERVICE = "user"; field public static final java.lang.String VIBRATOR_SERVICE = "vibrator"; + field public static final java.lang.String VR_SERVICE = "vrmanager"; field public static final java.lang.String WALLPAPER_SERVICE = "wallpaper"; field public static final java.lang.String WIFI_AWARE_SERVICE = "wifiaware"; field public static final java.lang.String WIFI_P2P_SERVICE = "wifip2p"; diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 5a75a673bf2ad..fc1d6136838e4 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -115,6 +115,7 @@ import android.print.PrintManager; import android.service.autofill.IAutoFillManagerService; import android.service.persistentdata.IPersistentDataBlockService; import android.service.persistentdata.PersistentDataBlockManager; +import android.service.vr.IVrManager; import android.telecom.TelecomManager; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; @@ -814,6 +815,14 @@ final class SystemServiceRegistry { IAutoFillManagerService service = IAutoFillManagerService.Stub.asInterface(b); return new AutoFillManager(ctx, service); }}); + + registerService(Context.VR_SERVICE, VrManager.class, new CachedServiceFetcher() { + @Override + public VrManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.VR_SERVICE); + return new VrManager(IVrManager.Stub.asInterface(b)); + } + }); } /** diff --git a/core/java/android/app/VrManager.java b/core/java/android/app/VrManager.java new file mode 100644 index 0000000000000..a0b0eeaaf1aac --- /dev/null +++ b/core/java/android/app/VrManager.java @@ -0,0 +1,44 @@ +package android.app; + + +import android.annotation.SystemApi; +import android.content.ComponentName; +import android.os.RemoteException; +import android.service.vr.IVrManager; + +/** + * Used to control aspects of a devices Virtual Reality (VR) capabilities. + *

+ * You do not instantiate this class directly; instead, retrieve it through + * {@link android.content.Context#getSystemService}. + * @hide + */ +@SystemApi +public class VrManager { + private final IVrManager mService; + + /** + * {@hide} + */ + public VrManager(IVrManager service) { + mService = service; + } + + /** + * Sets the persistent VR mode state of a device. When a device is in persistent VR mode it will + * remain in VR mode even if the foreground does not specify Vr mode being enabled. Mainly used + * by VR viewers to indicate that a device is placed in a VR viewer. + * + *

Requires {@link android.Manifest.permission#ACCESS_VR_MANAGER} permission.

+ * + * @see Activity#setVrModeEnabled(boolean, ComponentName) + * @param enabled true if the device should be placed in persistent VR mode. + */ + public void setPersistentVrModeEnabled(boolean enabled) { + try { + mService.setPersistentVrModeEnabled(enabled); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } +} diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index f610a292dcd34..a7d5ac6b219ad 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -35,6 +35,7 @@ import android.annotation.UserIdInt; import android.app.IApplicationThread; import android.app.IServiceConnection; import android.app.Notification; +import android.app.VrManager; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; @@ -3763,6 +3764,16 @@ public abstract class Context { */ public static final String OVERLAY_SERVICE = "overlay"; + /** + * Use with {@link #getSystemService} to retrieve a + * {@link VrManager} for accessing the VR service. + * + * @see #getSystemService + * @hide + */ + @SystemApi + public static final String VR_SERVICE = "vrmanager"; + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. diff --git a/core/java/android/service/vr/IVrManager.aidl b/core/java/android/service/vr/IVrManager.aidl index 62ecab3583cd2..10e4177847005 100644 --- a/core/java/android/service/vr/IVrManager.aidl +++ b/core/java/android/service/vr/IVrManager.aidl @@ -42,5 +42,13 @@ interface IVrManager { */ boolean getVrModeState(); + /** + * Sets the persistent VR mode state of a device. When a device is in persistent VR mode it will + * remain in VR mode even if the foreground does not specify VR mode being enabled. Mainly used + * by VR viewers to indicate that a device is placed in a VR viewer. + * + * @param enabled true if the device should be placed in persistent VR mode. + */ + void setPersistentVrModeEnabled(in boolean enabled); } diff --git a/services/core/java/com/android/server/vr/VrManagerInternal.java b/services/core/java/com/android/server/vr/VrManagerInternal.java index 0fc1900166e26..45b7baf8e8f5b 100644 --- a/services/core/java/com/android/server/vr/VrManagerInternal.java +++ b/services/core/java/com/android/server/vr/VrManagerInternal.java @@ -79,4 +79,13 @@ public abstract class VrManagerInternal { * given in {@link android.service.vr.VrModeException} on failure. */ public abstract int hasVrPackage(@NonNull ComponentName packageName, int userId); + + /** + * Sets the persistent VR mode state of a device. When a device is in persistent VR mode it will + * remain in VR mode even if the foreground does not specify Vr mode being enabled. Mainly used + * by VR viewers to indicate that a device is placed in a VR viewer. + * + * @param enabled true if the device should be placed in persistent VR mode. + */ + public abstract void setPersistentVrModeEnabled(boolean enabled); } diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java index 8368b05a19341..51c4ce3108011 100644 --- a/services/core/java/com/android/server/vr/VrManagerService.java +++ b/services/core/java/com/android/server/vr/VrManagerService.java @@ -20,15 +20,10 @@ import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.NotificationManager; import android.annotation.NonNull; -import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.ApplicationInfo; -import android.content.pm.FeatureInfo; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.os.Binder; @@ -53,7 +48,6 @@ import android.util.Slog; import android.util.SparseArray; import com.android.internal.R; -import com.android.server.LocalServices; import com.android.server.SystemConfig; import com.android.server.SystemService; import com.android.server.utils.ManagedApplicationService.PendingEvent; @@ -69,8 +63,6 @@ import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.Date; -import java.util.List; -import java.util.Map; import java.util.Objects; /** @@ -121,8 +113,10 @@ public class VrManagerService extends SystemService implements EnabledComponentC // State protected by mLock private boolean mVrModeAllowed; private boolean mVrModeEnabled; + private boolean mPersistentVrModeEnabled; private EnabledComponentsObserver mComponentObserver; private ManagedApplicationService mCurrentVrService; + private ComponentName mDefaultVrService; private Context mContext; private ComponentName mCurrentVrModeComponent; private int mCurrentVrModeUser; @@ -157,6 +151,10 @@ public class VrManagerService extends SystemService implements EnabledComponentC if (mVrModeAllowed) { consumeAndApplyPendingStateLocked(); } else { + // Disable persistent mode when VR mode isn't allowed, allows an escape hatch to + // exit persistent VR mode when screen is turned off. + mPersistentVrModeEnabled = false; + // Set pending state to current state. mPendingState = (mVrModeEnabled && mCurrentVrService != null) ? new VrState(mVrModeEnabled, mCurrentVrService.getComponent(), @@ -377,6 +375,12 @@ public class VrManagerService extends SystemService implements EnabledComponentC return VrManagerService.this.getVrMode(); } + @Override + public void setPersistentVrModeEnabled(boolean enabled) { + enforceCallerPermission(Manifest.permission.ACCESS_VR_MANAGER); + VrManagerService.this.setPersistentVrModeEnabled(enabled); + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP) @@ -387,6 +391,8 @@ public class VrManagerService extends SystemService implements EnabledComponentC } pw.println("********* Dump of VrManagerService *********"); pw.println("VR mode is currently: " + ((mVrModeAllowed) ? "allowed" : "disallowed")); + pw.println("Persistent VR mode is currently: " + + ((mPersistentVrModeEnabled) ? "enabled" : "disabled")); pw.println("Previous state transitions:\n"); String tab = " "; dumpStateTransitions(pw); @@ -462,6 +468,11 @@ public class VrManagerService extends SystemService implements EnabledComponentC public int hasVrPackage(ComponentName packageName, int userId) { return VrManagerService.this.hasVrPackage(packageName, userId); } + + @Override + public void setPersistentVrModeEnabled(boolean enabled) { + VrManagerService.this.setPersistentVrModeEnabled(enabled); + } } public VrManagerService(Context context) { @@ -494,6 +505,15 @@ public class VrManagerService extends SystemService implements EnabledComponentC mComponentObserver.rebuildAll(); } + + //TODO: something more robust than picking the first one + ArraySet defaultVrComponents = + SystemConfig.getInstance().getDefaultVrComponents(); + if (defaultVrComponents.size() > 0) { + mDefaultVrService = defaultVrComponents.valueAt(0); + } else { + Slog.i(TAG, "No default vr listener service found."); + } } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { synchronized (mLock) { mVrModeAllowed = true; @@ -637,8 +657,8 @@ public class VrManagerService extends SystemService implements EnabledComponentC } } + mCurrentVrModeComponent = calling; if (calling != null && !Objects.equals(calling, mCurrentVrModeComponent)) { - mCurrentVrModeComponent = calling; sendUpdatedCaller = true; } @@ -947,7 +967,25 @@ public class VrManagerService extends SystemService implements EnabledComponentC int userId, @NonNull ComponentName callingPackage) { synchronized (mLock) { - VrState pending = new VrState(enabled, targetPackageName, userId, callingPackage); + VrState pending; + ComponentName targetListener; + ComponentName foregroundVrComponent; + + // If the device is in persistent VR mode, then calls to disable VR mode are ignored, + // and the system default VR listener is used. + boolean targetEnabledState = enabled || mPersistentVrModeEnabled; + if (!enabled && mPersistentVrModeEnabled) { + targetListener = mDefaultVrService; + + // Current foreground component isn't a VR one (in 2D app case) + foregroundVrComponent = null; + } else { + targetListener = targetPackageName; + foregroundVrComponent = callingPackage; + } + pending = new VrState( + targetEnabledState, targetListener, userId, foregroundVrComponent); + if (!mVrModeAllowed) { // We're not allowed to be in VR mode. Make this state pending. This will be // applied the next time we are allowed to enter VR mode unless it is superseded by @@ -956,7 +994,7 @@ public class VrManagerService extends SystemService implements EnabledComponentC return; } - if (!enabled && mCurrentVrService != null) { + if (!targetEnabledState && mCurrentVrService != null) { // If we're transitioning out of VR mode, delay briefly to avoid expensive HAL calls // and service bind/unbind in case we are immediately switching to another VR app. if (mPendingState == null) { @@ -971,7 +1009,19 @@ public class VrManagerService extends SystemService implements EnabledComponentC mPendingState = null; } - updateCurrentVrServiceLocked(enabled, targetPackageName, userId, callingPackage); + updateCurrentVrServiceLocked( + targetEnabledState, targetListener, userId, foregroundVrComponent); + } + } + + private void setPersistentVrModeEnabled(boolean enabled) { + synchronized (mLock) { + mPersistentVrModeEnabled = enabled; + + // Disabling persistent mode when not showing a VR should disable the overall vr mode. + if (!enabled && mCurrentVrModeComponent == null) { + setVrMode(false, null, 0, null); + } } }