diff options
author | Ruben Brunk <rubenbrunk@google.com> | 2015-12-18 19:50:24 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2015-12-18 19:50:24 +0000 |
commit | 55bed957788e853d3ee3f674dd6eea79ad3a193b (patch) | |
tree | 1d9b968bbe157df2c2c3abba2b4d2f96f4b996ac | |
parent | 1d0d4d3c6e746d7dbe11a1ba77638ce44e7c8ddf (diff) | |
parent | dd18a0b69537954d1cc34929a1386deb54f12b14 (diff) |
Merge "Add a framework service tracking VR mode state."
17 files changed, 505 insertions, 97 deletions
diff --git a/api/current.txt b/api/current.txt index 8ad1f65dbcac..36430bb1e42d 100644 --- a/api/current.txt +++ b/api/current.txt @@ -3538,6 +3538,7 @@ package android.app { method public deprecated void setTitleColor(int); method public void setVisible(boolean); method public final void setVolumeControlStream(int); + method public void setVrMode(boolean); method public boolean shouldShowRequestPermissionRationale(java.lang.String); method public boolean shouldUpRecreateTask(android.content.Intent); method public boolean showAssist(android.os.Bundle); @@ -8993,6 +8994,7 @@ package android.content.pm { field public static final int FLAG_ALWAYS_RETAIN_TASK_STATE = 8; // 0x8 field public static final int FLAG_AUTO_REMOVE_FROM_RECENTS = 8192; // 0x2000 field public static final int FLAG_CLEAR_TASK_ON_LAUNCH = 4; // 0x4 + field public static final int FLAG_ENABLE_VR_MODE = 32768; // 0x8000 field public static final int FLAG_EXCLUDE_FROM_RECENTS = 32; // 0x20 field public static final int FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS = 256; // 0x100 field public static final int FLAG_FINISH_ON_TASK_LAUNCH = 2; // 0x2 @@ -9556,6 +9558,7 @@ package android.content.pm { field public static final java.lang.String FEATURE_USB_ACCESSORY = "android.hardware.usb.accessory"; field public static final java.lang.String FEATURE_USB_HOST = "android.hardware.usb.host"; field public static final java.lang.String FEATURE_VERIFIED_BOOT = "android.software.verified_boot"; + field public static final java.lang.String FEATURE_VR_MODE = "android.software.vr.mode"; field public static final java.lang.String FEATURE_WATCH = "android.hardware.type.watch"; field public static final java.lang.String FEATURE_WEBVIEW = "android.software.webview"; field public static final java.lang.String FEATURE_WIFI = "android.hardware.wifi"; @@ -33258,6 +33261,7 @@ package android.service.notification { method public final void requestInterruptionFilter(int); method public final void requestListenerHints(int); method public final void setNotificationsShown(java.lang.String[]); + field public static final java.lang.String CATEGORY_VR_NOTIFICATIONS = "android.intent.category.vr.notifications"; field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1 field public static final int INTERRUPTION_FILTER_ALARMS = 4; // 0x4 field public static final int INTERRUPTION_FILTER_ALL = 1; // 0x1 diff --git a/api/system-current.txt b/api/system-current.txt index b83990cbc5cd..1a1555579e07 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -3643,6 +3643,7 @@ package android.app { method public deprecated void setTitleColor(int); method public void setVisible(boolean); method public final void setVolumeControlStream(int); + method public void setVrMode(boolean); method public boolean shouldShowRequestPermissionRationale(java.lang.String); method public boolean shouldUpRecreateTask(android.content.Intent); method public boolean showAssist(android.os.Bundle); @@ -9261,6 +9262,7 @@ package android.content.pm { field public static final int FLAG_ALWAYS_RETAIN_TASK_STATE = 8; // 0x8 field public static final int FLAG_AUTO_REMOVE_FROM_RECENTS = 8192; // 0x2000 field public static final int FLAG_CLEAR_TASK_ON_LAUNCH = 4; // 0x4 + field public static final int FLAG_ENABLE_VR_MODE = 32768; // 0x8000 field public static final int FLAG_EXCLUDE_FROM_RECENTS = 32; // 0x20 field public static final int FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS = 256; // 0x100 field public static final int FLAG_FINISH_ON_TASK_LAUNCH = 2; // 0x2 @@ -9872,6 +9874,7 @@ package android.content.pm { field public static final java.lang.String FEATURE_USB_ACCESSORY = "android.hardware.usb.accessory"; field public static final java.lang.String FEATURE_USB_HOST = "android.hardware.usb.host"; field public static final java.lang.String FEATURE_VERIFIED_BOOT = "android.software.verified_boot"; + field public static final java.lang.String FEATURE_VR_MODE = "android.software.vr.mode"; field public static final java.lang.String FEATURE_WATCH = "android.hardware.type.watch"; field public static final java.lang.String FEATURE_WEBVIEW = "android.software.webview"; field public static final java.lang.String FEATURE_WIFI = "android.hardware.wifi"; @@ -35406,6 +35409,7 @@ package android.service.notification { method public final void setNotificationsShown(java.lang.String[]); method public final void setOnNotificationPostedTrim(int); method public void unregisterAsSystemService() throws android.os.RemoteException; + field public static final java.lang.String CATEGORY_VR_NOTIFICATIONS = "android.intent.category.vr.notifications"; field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1 field public static final int INTERRUPTION_FILTER_ALARMS = 4; // 0x4 field public static final int INTERRUPTION_FILTER_ALL = 1; // 0x1 diff --git a/api/test-current.txt b/api/test-current.txt index 0cb6bc52461d..3531e34a2407 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -3538,6 +3538,7 @@ package android.app { method public deprecated void setTitleColor(int); method public void setVisible(boolean); method public final void setVolumeControlStream(int); + method public void setVrMode(boolean); method public boolean shouldShowRequestPermissionRationale(java.lang.String); method public boolean shouldUpRecreateTask(android.content.Intent); method public boolean showAssist(android.os.Bundle); @@ -8993,6 +8994,7 @@ package android.content.pm { field public static final int FLAG_ALWAYS_RETAIN_TASK_STATE = 8; // 0x8 field public static final int FLAG_AUTO_REMOVE_FROM_RECENTS = 8192; // 0x2000 field public static final int FLAG_CLEAR_TASK_ON_LAUNCH = 4; // 0x4 + field public static final int FLAG_ENABLE_VR_MODE = 32768; // 0x8000 field public static final int FLAG_EXCLUDE_FROM_RECENTS = 32; // 0x20 field public static final int FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS = 256; // 0x100 field public static final int FLAG_FINISH_ON_TASK_LAUNCH = 2; // 0x2 @@ -9556,6 +9558,7 @@ package android.content.pm { field public static final java.lang.String FEATURE_USB_ACCESSORY = "android.hardware.usb.accessory"; field public static final java.lang.String FEATURE_USB_HOST = "android.hardware.usb.host"; field public static final java.lang.String FEATURE_VERIFIED_BOOT = "android.software.verified_boot"; + field public static final java.lang.String FEATURE_VR_MODE = "android.software.vr.mode"; field public static final java.lang.String FEATURE_WATCH = "android.hardware.type.watch"; field public static final java.lang.String FEATURE_WEBVIEW = "android.software.webview"; field public static final java.lang.String FEATURE_WIFI = "android.hardware.wifi"; @@ -33260,6 +33263,7 @@ package android.service.notification { method public final void requestInterruptionFilter(int); method public final void requestListenerHints(int); method public final void setNotificationsShown(java.lang.String[]); + field public static final java.lang.String CATEGORY_VR_NOTIFICATIONS = "android.intent.category.vr.notifications"; field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1 field public static final int INTERRUPTION_FILTER_ALARMS = 4; // 0x4 field public static final int INTERRUPTION_FILTER_ALL = 1; // 0x1 diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 61fd952d8f73..6d72059e0a47 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -6038,6 +6038,22 @@ public class Activity extends ContextThemeWrapper } /** + * Enable or disable virtual reality (VR) mode. + * + * <p>VR mode is a hint to Android system services to switch to modes optimized for + * high-performance stereoscopic rendering.</p> + * + * @param enabled {@code true} to enable this mode. + */ + public void setVrMode(boolean enabled) { + try { + ActivityManagerNative.getDefault().setVrMode(mToken, enabled); + } catch (RemoteException e) { + // pass + } + } + + /** * Start an action mode of the default type {@link ActionMode#TYPE_PRIMARY}. * * @param callback Callback that will manage lifecycle events for this action mode diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index d724823843d7..1b0827331903 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -2766,6 +2766,14 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM reply.writeNoException(); return true; } + case SET_VR_MODE_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + final IBinder token = data.readStrongBinder(); + final boolean enable = data.readInt() == 1; + setVrMode(token, enable); + reply.writeNoException(); + return true; + } } return super.onTransact(code, data, reply, flags); @@ -5896,6 +5904,18 @@ class ActivityManagerProxy implements IActivityManager return res; } + public void setVrMode(IBinder token, boolean enabled) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(token); + data.writeInt(enabled ? 1 : 0); + mRemote.transact(SET_VR_MODE_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + @Override public IActivityContainer createStackOnDisplay(int displayId) throws RemoteException { Parcel data = Parcel.obtain(); diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index f91a0bef7833..0ecf2231665a 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -552,6 +552,8 @@ public interface IActivityManager extends IInterface { public void enterPictureInPictureMode(IBinder token) throws RemoteException; + public void setVrMode(IBinder token, boolean enabled) throws RemoteException; + /* * Private non-Binder interfaces */ @@ -917,4 +919,5 @@ public interface IActivityManager extends IInterface { int IN_PICTURE_IN_PICTURE_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 353; int KILL_PACKAGE_DEPENDENTS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 354; int ENTER_PICTURE_IN_PICTURE_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 355; + int SET_VR_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 356; } diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 0cb0e9fce251..326735ec4f36 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -276,6 +276,12 @@ public class ActivityInfo extends ComponentInfo */ public static final int FLAG_RESUME_WHILE_PAUSING = 0x4000; /** + * Bit in {@link #flags} indicating that this activity should be run with VR mode enabled. + * + * {@see android.app.Activity#setVrMode(boolean)}. + */ + public static final int FLAG_ENABLE_VR_MODE = 0x8000; + /** * @hide Bit in {@link #flags}: If set, this component will only be seen * by the system user. Only works with broadcast receivers. Set from the * android.R.attr#systemUserOnly attribute. diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 40bcc7ed9cde..3235bcff1eaf 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1830,6 +1830,16 @@ public abstract class PackageManager { public static final String FEATURE_MIDI = "android.software.midi"; /** + * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: + * The device implements a an optimized mode for virtual reality (VR) applications that handles + * stereoscopic rendering of notifications, and may potentially also include optimizations to + * reduce latency in the graphics, display, and sensor stacks. Presence of this feature + * also indicates that the VrCore library is included on this device. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_VR_MODE = "android.software.vr.mode"; + + /** * Action to external storage service to clean out removed apps. * @hide */ diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 232d4fe4e979..1e62edc6634f 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -60,6 +60,16 @@ import java.util.List; * <action android:name="android.service.notification.NotificationListenerService" /> * </intent-filter> * </service></pre> + * <p> Typically, while enabled in user settings, this service will be bound on boot or when a + * settings change occurs that could affect whether this service should run. However, for some + * system usage modes, the you may instead specify that this service is instead bound and unbound + * in response to mode changes by including a category in the intent filter. Currently + * supported categories are: + * <ul> + * <li>{@link #CATEGORY_VR_NOTIFICATIONS} - this service is bound when an Activity has enabled + * VR mode. {@see android.app.Activity#setVrMode(boolean)}.</li> + * </ul> + * </p> */ public abstract class NotificationListenerService extends Service { // TAG = "NotificationListenerService[MySubclass]" @@ -162,6 +172,17 @@ public abstract class NotificationListenerService extends Service { = "android.service.notification.NotificationListenerService"; /** + * If this category is declared in the application manifest for a service of this type, this + * service will be bound when VR mode is enabled, and unbound when VR mode is disabled rather + * than the normal lifecycle for a notification service. + * + * {@see android.app.Activity#setVrMode(boolean)} + */ + @SdkConstant(SdkConstant.SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_VR_NOTIFICATIONS = + "android.intent.category.vr.notifications"; + + /** * Implement this method to learn about new notifications as they are posted by apps. * * @param sbn A data structure encapsulating the original {@link android.app.Notification} diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 3e7b963ddd98..de0a23aa723f 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -53,6 +53,7 @@ import com.android.server.am.ActivityStack.ActivityState; import com.android.server.firewall.IntentFirewall; import com.android.server.pm.Installer; import com.android.server.statusbar.StatusBarManagerInternal; +import com.android.server.vr.VrManagerInternal; import com.android.server.wm.AppTransition; import com.android.server.wm.WindowManagerService; @@ -1443,6 +1444,7 @@ public final class ActivityManagerService extends ActivityManagerNative static final int IDLE_UIDS_MSG = 60; static final int SYSTEM_USER_UNLOCK_MSG = 61; static final int LOG_STACK_STATE = 62; + static final int VR_MODE_CHANGE_MSG = 63; static final int FIRST_ACTIVITY_STACK_MSG = 100; static final int FIRST_BROADCAST_QUEUE_MSG = 200; @@ -2157,6 +2159,10 @@ public final class ActivityManagerService extends ActivityManagerNative mStackSupervisor.logStackState(); } } break; + case VR_MODE_CHANGE_MSG: { + VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class); + vrService.setVrMode(msg.arg1 != 0); + } break; } } }; @@ -2781,6 +2787,7 @@ public final class ActivityManagerService extends ActivityManagerNative mWindowManager.setFocusedApp(r.appToken, true); } applyUpdateLockStateLocked(r); + applyUpdateVrModeLocked(r); if (mFocusedActivity.userId != mLastFocusedUserId) { mHandler.removeMessages(FOREGROUND_PROFILE_CHANGED_MSG); mHandler.obtainMessage( @@ -2877,6 +2884,11 @@ public final class ActivityManagerService extends ActivityManagerNative mHandler.obtainMessage(IMMERSIVE_MODE_LOCK_MSG, (nextState) ? 1 : 0, 0, r)); } + final void applyUpdateVrModeLocked(ActivityRecord r) { + mHandler.sendMessage( + mHandler.obtainMessage(VR_MODE_CHANGE_MSG, (r.isVrActivity) ? 1 : 0, 0)); + } + final void showAskCompatModeDialogLocked(ActivityRecord r) { Message msg = Message.obtain(); msg.what = SHOW_COMPAT_MODE_DIALOG_UI_MSG; @@ -11750,6 +11762,26 @@ public final class ActivityManagerService extends ActivityManagerNative } } + @Override + public void setVrMode(IBinder token, boolean enabled) { + if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) { + throw new UnsupportedOperationException("VR mode not supported on this device!"); + } + + synchronized(this) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r == null) { + throw new IllegalArgumentException(); + } + r.isVrActivity = enabled; + + // Update associated state if this activity is currently focused + if (r == mFocusedActivity) { + applyUpdateVrModeLocked(r); + } + } + } + public boolean isTopActivityImmersive() { enforceNotIsolatedCaller("startActivity"); synchronized (this) { diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 3da0f8d9c778..4d9120b09478 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -181,6 +181,7 @@ final class ActivityRecord { boolean forceNewConfig; // force re-create with new config next time int launchCount; // count of launches since last state long lastLaunchTime; // time of last lauch of this activity + boolean isVrActivity; // is the activity running in VR mode? ArrayList<ActivityContainer> mChildContainers = new ArrayList<ActivityContainer>(); String stringName; // for caching of toString(). @@ -317,6 +318,7 @@ final class ActivityRecord { pw.print(" forceNewConfig="); pw.println(forceNewConfig); pw.print(prefix); pw.print("mActivityType="); pw.println(activityTypeToString(mActivityType)); + pw.print(prefix); pw.print("vrMode="); pw.println(isVrActivity); if (displayStartTime != 0 || startTime != 0) { pw.print(prefix); pw.print("displayStartTime="); if (displayStartTime == 0) pw.print("0"); @@ -650,6 +652,7 @@ final class ActivityRecord { } immersive = (aInfo.flags & ActivityInfo.FLAG_IMMERSIVE) != 0; + isVrActivity = (aInfo.flags & ActivityInfo.FLAG_ENABLE_VR_MODE) != 0; } else { realActivity = null; taskAffinity = null; @@ -661,6 +664,7 @@ final class ActivityRecord { noDisplay = false; mActivityType = APPLICATION_ACTIVITY_TYPE; immersive = false; + isVrActivity = false; } } diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index d5773690f122..b7662daffec4 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -42,6 +42,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.text.TextUtils; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.Slog; @@ -53,6 +54,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map.Entry; import java.util.Objects; import java.util.Set; @@ -93,6 +95,8 @@ abstract public class ManagedServices { // List of packages in restored setting across all mUserProfiles, for quick // filtering upon package updates. private ArraySet<String> mRestoredPackages = new ArraySet<>(); + // State of current service categories + private ArrayMap<String, Boolean> mCategoryEnabled = new ArrayMap<>(); // Kept to de-dupe user change events (experienced after boot, when we receive a settings and a @@ -262,6 +266,46 @@ abstract public class ManagedServices { } } + public void setCategoryState(String category, boolean enabled) { + synchronized (mMutex) { + final Boolean previous = mCategoryEnabled.put(category, enabled); + if (!(previous == null || previous != enabled)) { + return; + } + + // State changed + if (DEBUG) { + Slog.d(TAG, ((enabled) ? "Enabling " : "Disabling ") + "category " + category); + } + + final int[] userIds = mUserProfiles.getCurrentProfileIds(); + for (int userId : userIds) { + final Set<ComponentName> componentNames = queryPackageForServices(null, + userId, category); + + // Disallow services not enabled in Settings + final ArraySet<ComponentName> userComponents = + loadComponentNamesFromSetting(mConfig.secureSettingName, userId); + if (userComponents == null) { + componentNames.clear(); + } else { + componentNames.retainAll(userComponents); + } + + if (DEBUG) { + Slog.d(TAG, "Components for category " + category + ": " + componentNames); + } + for (ComponentName c : componentNames) { + if (enabled) { + registerServiceLocked(c, userId); + } else { + unregisterServiceLocked(c, userId); + } + } + } + + } + } private void rebuildRestoredPackages() { mRestoredPackages.clear(); @@ -283,9 +327,9 @@ abstract public class ManagedServices { int userId) { final ContentResolver cr = mContext.getContentResolver(); String settingValue = Settings.Secure.getStringForUser( - cr, - settingName, - userId); + cr, + settingName, + userId); if (TextUtils.isEmpty(settingValue)) return null; String[] restored = settingValue.split(ENABLED_SERVICES_SEPARATOR); @@ -314,10 +358,10 @@ abstract public class ManagedServices { TextUtils.join(ENABLED_SERVICES_SEPARATOR, componentNames); final ContentResolver cr = mContext.getContentResolver(); Settings.Secure.putStringForUser( - cr, - settingName, - value, - userId); + cr, + settingName, + value, + userId); } /** @@ -333,12 +377,20 @@ abstract public class ManagedServices { } protected Set<ComponentName> queryPackageForServices(String packageName, int userId) { + return queryPackageForServices(packageName, userId, null); + } + + protected Set<ComponentName> queryPackageForServices(String packageName, int userId, + String category) { Set<ComponentName> installed = new ArraySet<>(); final PackageManager pm = mContext.getPackageManager(); Intent queryIntent = new Intent(mConfig.serviceInterface); if (!TextUtils.isEmpty(packageName)) { queryIntent.setPackage(packageName); } + if (category != null) { + queryIntent.addCategory(category); + } List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser( queryIntent, PackageManager.GET_SERVICES | PackageManager.GET_META_DATA, @@ -353,9 +405,9 @@ abstract public class ManagedServices { ComponentName component = new ComponentName(info.packageName, info.name); if (!mConfig.bindPermission.equals(info.permission)) { Slog.w(TAG, "Skipping " + getCaption() + " service " - + info.packageName + "/" + info.name - + ": it does not require the permission " - + mConfig.bindPermission); + + info.packageName + "/" + info.name + + ": it does not require the permission " + + mConfig.bindPermission); continue; } installed.add(component); @@ -449,6 +501,16 @@ abstract public class ManagedServices { } final ArrayList<ComponentName> add = new ArrayList<>(userComponents); + + // Remove components from disabled categories so that those services aren't run. + for (Entry<String, Boolean> e : mCategoryEnabled.entrySet()) { + if (!e.getValue()) { + Set<ComponentName> c = queryPackageForServices(null, userIds[i], + e.getKey()); + add.removeAll(c); + } + } + toAdd.put(userIds[i], add); newEnabled.addAll(userComponents); @@ -488,93 +550,97 @@ abstract public class ManagedServices { * Version of registerService that takes the name of a service component to bind to. */ private void registerService(final ComponentName name, final int userid) { + synchronized (mMutex) { + registerServiceLocked(name, userid); + } + } + + private void registerServiceLocked(final ComponentName name, final int userid) { if (DEBUG) Slog.v(TAG, "registerService: " + name + " u=" + userid); - synchronized (mMutex) { - final String servicesBindingTag = name.toString() + "/" + userid; - if (mServicesBinding.contains(servicesBindingTag)) { - // stop registering this thing already! we're working on it - return; - } - mServicesBinding.add(servicesBindingTag); + final String servicesBindingTag = name.toString() + "/" + userid; + if (mServicesBinding.contains(servicesBindingTag)) { + // stop registering this thing already! we're working on it + return; + } + mServicesBinding.add(servicesBindingTag); - final int N = mServices.size(); - for (int i = N - 1; i >= 0; i--) { - final ManagedServiceInfo info = mServices.get(i); - if (name.equals(info.component) - && info.userid == userid) { - // cut old connections - if (DEBUG) Slog.v(TAG, " disconnecting old " + getCaption() + ": " - + info.service); - removeServiceLocked(i); - if (info.connection != null) { - mContext.unbindService(info.connection); - } + final int N = mServices.size(); + for (int i = N - 1; i >= 0; i--) { + final ManagedServiceInfo info = mServices.get(i); + if (name.equals(info.component) + && info.userid == userid) { + // cut old connections + if (DEBUG) Slog.v(TAG, " disconnecting old " + getCaption() + ": " + + info.service); + removeServiceLocked(i); + if (info.connection != null) { + mContext.unbindService(info.connection); } } + } - Intent intent = new Intent(mConfig.serviceInterface); - intent.setComponent(name); + Intent intent = new Intent(mConfig.serviceInterface); + intent.setComponent(name); - intent.putExtra(Intent.EXTRA_CLIENT_LABEL, mConfig.clientLabel); + intent.putExtra(Intent.EXTRA_CLIENT_LABEL, mConfig.clientLabel); - final PendingIntent pendingIntent = PendingIntent.getActivity( - mContext, 0, new Intent(mConfig.settingsAction), 0); - intent.putExtra(Intent.EXTRA_CLIENT_INTENT, pendingIntent); + final PendingIntent pendingIntent = PendingIntent.getActivity( + mContext, 0, new Intent(mConfig.settingsAction), 0); + intent.putExtra(Intent.EXTRA_CLIENT_INTENT, pendingIntent); - ApplicationInfo appInfo = null; - try { - appInfo = mContext.getPackageManager().getApplicationInfo( - name.getPackageName(), 0); - } catch (NameNotFoundException e) { - // Ignore if the package doesn't exist we won't be able to bind to the service. - } - final int targetSdkVersion = - appInfo != null ? appInfo.targetSdkVersion : Build.VERSION_CODES.BASE; + ApplicationInfo appInfo = null; + try { + appInfo = mContext.getPackageManager().getApplicationInfo( + name.getPackageName(), 0); + } catch (NameNotFoundException e) { + // Ignore if the package doesn't exist we won't be able to bind to the service. + } + final int targetSdkVersion = + appInfo != null ? appInfo.targetSdkVersion : Build.VERSION_CODES.BASE; - try { - if (DEBUG) Slog.v(TAG, "binding: " + intent); - ServiceConnection serviceConnection = new ServiceConnection() { - IInterface mService; - - @Override - public void onServiceConnected(ComponentName name, IBinder binder) { - boolean added = false; - ManagedServiceInfo info = null; - synchronized (mMutex) { - mServicesBinding.remove(servicesBindingTag); - try { - mService = asInterface(binder); - info = newServiceInfo(mService, name, - userid, false /*isSystem*/, this, targetSdkVersion); - binder.linkToDeath(info, 0); - added = mServices.add(info); - } catch (RemoteException e) { - // already dead - } - } - if (added) { - onServiceAdded(info); + try { + if (DEBUG) Slog.v(TAG, "binding: " + intent); + ServiceConnection serviceConnection = new ServiceConnection() { + IInterface mService; + + @Override + public void onServiceConnected(ComponentName name, IBinder binder) { + boolean added = false; + ManagedServiceInfo info = null; + synchronized (mMutex) { + mServicesBinding.remove(servicesBindingTag); + try { + mService = asInterface(binder); + info = newServiceInfo(mService, name, + userid, false /*isSystem*/, this, targetSdkVersion); + binder.linkToDeath(info, 0); + added = mServices.add(info); + } catch (RemoteException e) { + // already dead } } - - @Override - public void onServiceDisconnected(ComponentName name) { - Slog.v(TAG, getCaption() + " connection lost: " + name); + if (added) { + onServiceAdded(info); } - }; - if (!mContext.bindServiceAsUser(intent, - serviceConnection, - Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, - new UserHandle(userid))) { - mServicesBinding.remove(servicesBindingTag); - Slog.w(TAG, "Unable to bind " + getCaption() + " service: " + intent); - return; } - } catch (SecurityException ex) { - Slog.e(TAG, "Unable to bind " + getCaption() + " service: " + intent, ex); + + @Override + public void onServiceDisconnected(ComponentName name) { + Slog.v(TAG, getCaption() + " connection lost: " + name); + } + }; + if (!mContext.bindServiceAsUser(intent, + serviceConnection, + Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, + new UserHandle(userid))) { + mServicesBinding.remove(servicesBindingTag); + Slog.w(TAG, "Unable to bind " + getCaption() + " service: " + intent); return; } + } catch (SecurityException ex) { + Slog.e(TAG, "Unable to bind " + getCaption() + " service: " + intent, ex); + return; } } @@ -583,20 +649,24 @@ abstract public class ManagedServices { */ private void unregisterService(ComponentName name, int userid) { synchronized (mMutex) { - final int N = mServices.size(); - for (int i = N - 1; i >= 0; i--) { - final ManagedServiceInfo info = mServices.get(i); - if (name.equals(info.component) - && info.userid == userid) { - removeServiceLocked(i); - if (info.connection != null) { - try { - mContext.unbindService(info.connection); - } catch (IllegalArgumentException ex) { - // something happened to the service: we think we have a connection - // but it's bogus. - Slog.e(TAG, getCaption() + " " + name + " could not be unbound: " + ex); - } + unregisterServiceLocked(name, userid); + } + } + + private void unregisterServiceLocked(ComponentName name, int userid) { + final int N = mServices.size(); + for (int i = N - 1; i >= 0; i--) { + final ManagedServiceInfo info = mServices.get(i); + if (name.equals(info.component) + && info.userid == userid) { + removeServiceLocked(i); + if (info.connection != null) { + try { + mContext.unbindService(info.connection); + } catch (IllegalArgumentException ex) { + // something happened to the service: we think we have a connection + // but it's bogus. + Slog.e(TAG, getCaption() + " " + name + " could not be unbound: " + ex); } } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 0bfbd7f5cd9f..80fb15d66999 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -124,6 +124,9 @@ import com.android.server.lights.LightsManager; import com.android.server.notification.ManagedServices.ManagedServiceInfo; import com.android.server.notification.ManagedServices.UserProfiles; import com.android.server.statusbar.StatusBarManagerInternal; +import com.android.server.vr.VrManagerInternal; +import com.android.server.vr.VrStateListener; + import libcore.io.IoUtils; import org.json.JSONArray; import org.json.JSONException; @@ -206,6 +209,8 @@ public class NotificationManagerService extends SystemService { AudioManagerInternal mAudioManagerInternal; StatusBarManagerInternal mStatusBar; Vibrator mVibrator; + private VrManagerInternal mVrManagerInternal; + private final NotificationVrListener mVrListener = new NotificationVrListener(); final IBinder mForegroundToken = new Binder(); private WorkerHandler mHandler; @@ -816,6 +821,14 @@ public class NotificationManagerService extends SystemService { } } + private final class NotificationVrListener extends VrStateListener { + @Override + public void onVrStateChanged(final boolean enabled) { + mListeners.setCategoryState(NotificationListenerService.CATEGORY_VR_NOTIFICATIONS, + enabled); + } + } + private SettingsObserver mSettingsObserver; private ZenModeHelper mZenModeHelper; @@ -1004,6 +1017,8 @@ public class NotificationManagerService extends SystemService { // Grab our optional AudioService mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); mAudioManagerInternal = getLocalService(AudioManagerInternal.class); + mVrManagerInternal = getLocalService(VrManagerInternal.class); + mVrManagerInternal.registerListener(mVrListener); mZenModeHelper.onSystemReady(); } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { // This observer will force an update when observe is called, causing us to diff --git a/services/core/java/com/android/server/vr/VrManagerInternal.java b/services/core/java/com/android/server/vr/VrManagerInternal.java new file mode 100644 index 000000000000..42db364e6acc --- /dev/null +++ b/services/core/java/com/android/server/vr/VrManagerInternal.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2015 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.server.vr; + +/** + * VR mode local system service interface. + * + * @hide Only for use within system server. + */ +public abstract class VrManagerInternal { + + /** + * Return current VR mode state. + * + * @return {@code true} if VR mode is enabled. + */ + public abstract boolean isInVrMode(); + + /** + * Set the current VR mode state. + * + * @param enabled {@code true} to enable VR mode. + */ + public abstract void setVrMode(boolean enabled); + + /** + * Add a listener for VR mode state changes. + * <p> + * This listener will immediately be called with the current VR mode state. + * </p> + * @param listener the listener instance to add. + */ + public abstract void registerListener(VrStateListener listener); + + /** + * Remove the listener from the current set of listeners. + * + * @param listener the listener to remove. + */ + public abstract void unregisterListener(VrStateListener listener); + +} diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java new file mode 100644 index 000000000000..9a55e7f47dac --- /dev/null +++ b/services/core/java/com/android/server/vr/VrManagerService.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2015 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.server.vr; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.UserHandle; +import android.util.ArraySet; +import android.util.Slog; + +import com.android.server.SystemService; + +import java.util.ArrayList; + +/** + * Service tracking whether VR mode is active, and notifying listening system services of state + * changes. + * + * {@hide} + */ +public class VrManagerService extends SystemService { + + public static final boolean DEBUG = false; + public static final String TAG = "VrManagerService"; + + private final Object mLock = new Object(); + private boolean mVrModeEnabled = false; + private ArraySet<VrStateListener> mListeners = new ArraySet<>(); + + private final class LocalService extends VrManagerInternal { + @Override + public boolean isInVrMode() { + return VrManagerService.this.getVrMode(); + } + + @Override + public void setVrMode(boolean enabled) { + VrManagerService.this.setVrMode(enabled); + } + + @Override + public void registerListener(VrStateListener listener) { + VrManagerService.this.addListener(listener); + } + + @Override + public void unregisterListener(VrStateListener listener) { + VrManagerService.this.removeListener(listener); + } + } + + public VrManagerService(Context context) { + super(context); + } + + @Override + public void onStart() { + publishLocalService(VrManagerInternal.class, new LocalService()); + } + + private void addListener(VrStateListener listener) { + synchronized (mLock) { + mListeners.add(listener); + } + } + + private void removeListener(VrStateListener listener) { + synchronized (mLock) { + mListeners.remove(listener); + } + } + + private void setVrMode(boolean enabled) { + synchronized (mLock) { + if (mVrModeEnabled != enabled) { + mVrModeEnabled = enabled; + if (DEBUG) Slog.d(TAG, "VR mode " + ((mVrModeEnabled) ? "enabled" : "disabled")); + onVrModeChangedLocked(); + } + } + } + + private boolean getVrMode() { + synchronized (mLock) { + return mVrModeEnabled; + } + } + + /** + * Notify system services of VR mode change. + */ + private void onVrModeChangedLocked() { + for (VrStateListener l : mListeners) { + l.onVrStateChanged(mVrModeEnabled); + } + } +} diff --git a/services/core/java/com/android/server/vr/VrStateListener.java b/services/core/java/com/android/server/vr/VrStateListener.java new file mode 100644 index 000000000000..b8af4b2d4425 --- /dev/null +++ b/services/core/java/com/android/server/vr/VrStateListener.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2015 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.server.vr; + +/** + * Listener for state changes in VrManagerService, + */ +public abstract class VrStateListener { + + /** + * Called when the VR mode state changes. + * + * @param enabled {@code true} if VR mode is enabled. + */ + public abstract void onVrStateChanged(boolean enabled); +} diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index fb5f21ab9ca6..3db8376e5fca 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -89,14 +89,13 @@ import com.android.server.trust.TrustManagerService; import com.android.server.tv.TvInputManagerService; import com.android.server.twilight.TwilightService; import com.android.server.usage.UsageStatsService; -import com.android.server.usb.UsbService; +import com.android.server.vr.VrManagerService; import com.android.server.wallpaper.WallpaperManagerService; import com.android.server.webkit.WebViewUpdateService; import com.android.server.wm.WindowManagerService; import dalvik.system.VMRuntime; -import java.io.File; import java.util.Locale; import java.util.Timer; import java.util.TimerTask; @@ -447,6 +446,7 @@ public final class SystemServer { ConsumerIrService consumerIr = null; MmsServiceBroker mmsService = null; EntropyMixer entropyMixer = null; + VrManagerService vrManagerService = null; boolean disableStorage = SystemProperties.getBoolean("config.disable_storage", false); boolean disableBluetooth = SystemProperties.getBoolean("config.disable_bluetooth", false); @@ -532,6 +532,10 @@ public final class SystemServer { ServiceManager.addService(Context.INPUT_SERVICE, inputManager); Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); + traceBeginAndSlog("StartVrManagerService"); + mSystemServiceManager.startService(VrManagerService.class); + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); + mActivityManagerService.setWindowManager(wm); inputManager.setWindowManagerCallbacks(wm.getInputMonitor()); |