Loading core/java/android/app/ActivityManager.java +29 −0 Original line number Original line Diff line number Diff line Loading @@ -4060,6 +4060,10 @@ public class ActivityManager { * thread can be a VR thread in a process at a time, and that thread may be subject to * thread can be a VR thread in a process at a time, and that thread may be subject to * restrictions on the amount of time it can run. * restrictions on the amount of time it can run. * * * If persistent VR mode is set, whatever thread has been granted aggressive scheduling via this * method will return to normal operation, and calling this method will do nothing while * persistent VR mode is enabled. * * To reset the VR thread for an application, a tid of 0 can be passed. * To reset the VR thread for an application, a tid of 0 can be passed. * * * @see android.os.Process#myTid() * @see android.os.Process#myTid() Loading @@ -4073,6 +4077,31 @@ public class ActivityManager { } } } } /** * Enable more aggressive scheduling for latency-sensitive low-runtime VR threads that persist * beyond a single process. It requires holding the * {@link android.Manifest.permission#RESTRICTED_VR_ACCESS} permission. Only one thread can be a * persistent VR thread at a time, and that thread may be subject to restrictions on the amount * of time it can run. Calling this method will disable aggressive scheduling for non-persistent * VR threads set via {@link #setVrThread}. If persistent VR mode is disabled then the * persistent VR thread loses its new scheduling priority; this method must be called again to * set the persistent thread. * * To reset the persistent VR thread, a tid of 0 can be passed. * * @see android.os.Process#myTid() * @param tid tid of the VR thread * @hide */ @RequiresPermission(Manifest.permission.RESTRICTED_VR_ACCESS) public static void setPersistentVrThread(int tid) { try { getService().setPersistentVrThread(tid); } catch (RemoteException e) { // pass } } /** /** * The AppTask allows you to manage your own application's tasks. * The AppTask allows you to manage your own application's tasks. * See {@link android.app.ActivityManager#getAppTasks()} * See {@link android.app.ActivityManager#getAppTasks()} Loading core/java/android/app/IActivityManager.aidl +1 −0 Original line number Original line Diff line number Diff line Loading @@ -605,6 +605,7 @@ interface IActivityManager { ActivityManager.TaskSnapshot getTaskSnapshot(int taskId); ActivityManager.TaskSnapshot getTaskSnapshot(int taskId); void scheduleApplicationInfoChanged(in List<String> packageNames, int userId); void scheduleApplicationInfoChanged(in List<String> packageNames, int userId); void setPersistentVrThread(int tid); // WARNING: when these transactions are updated, check if they are any callers on the native // WARNING: when these transactions are updated, check if they are any callers on the native // side. If so, make sure they are using the correct transaction ids and arguments. // side. If so, make sure they are using the correct transaction ids and arguments. Loading core/tests/coretests/AndroidManifest.xml +3 −0 Original line number Original line Diff line number Diff line Loading @@ -115,6 +115,9 @@ <!-- accessibility test permissions --> <!-- accessibility test permissions --> <uses-permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT" /> <uses-permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT" /> <!-- vr test permissions --> <uses-permission android:name="android.permission.RESTRICTED_VR_ACCESS" /> <application android:theme="@style/Theme" android:supportsRtl="true"> <application android:theme="@style/Theme" android:supportsRtl="true"> <uses-library android:name="android.test.runner" /> <uses-library android:name="android.test.runner" /> <uses-library android:name="org.apache.http.legacy" android:required="false" /> <uses-library android:name="org.apache.http.legacy" android:required="false" /> Loading core/tests/coretests/src/android/os/SetPersistentVrThreadTest.java 0 → 100644 +162 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2017 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 android.os; import android.app.ActivityManager; import android.app.VrManager; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; import android.os.Process; import android.provider.Settings; import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.SmallTest; import android.util.Log; /** * Tests ActivityManager#setPersistentVrThread and ActivityManager#setVrThread's * interaction with persistent VR mode. */ public class SetPersistentVrThreadTest extends ActivityInstrumentationTestCase2<TestVrActivity> { private TestVrActivity mActivity; private ActivityManager mActivityManager; private VrManager mVrManager; private Context mContext; private String mOldVrListener; private ComponentName mRequestedComponent; private static final int SCHED_OTHER = 0; private static final int SCHED_FIFO = 1; private static final int SCHED_RESET_ON_FORK = 0x40000000; public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners"; private static final String TAG = "VrSetPersistentFIFOThreadTest"; public SetPersistentVrThreadTest() { super(TestVrActivity.class); } @Override protected void setUp() throws Exception { super.setUp(); mContext = getInstrumentation().getTargetContext(); mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); mVrManager = (VrManager) mContext.getSystemService(Context.VR_SERVICE); mRequestedComponent = new ComponentName(mContext, TestVrActivity.TestVrListenerService.class); mOldVrListener = Settings.Secure.getString(mContext.getContentResolver(), ENABLED_VR_LISTENERS); Settings.Secure.putString(mContext.getContentResolver(), ENABLED_VR_LISTENERS, mRequestedComponent.flattenToString()); mActivity = getActivity(); } @Override protected void tearDown() throws Exception { try { setPersistentVrModeEnabled(false); } catch (Throwable e) { // pass } Settings.Secure.putString(mContext.getContentResolver(), ENABLED_VR_LISTENERS, mOldVrListener); super.tearDown(); } private void setPersistentVrModeEnabled(boolean enable) throws Throwable { mVrManager.setPersistentVrModeEnabled(enable); // Allow the system time to send out callbacks for persistent VR mode. Thread.sleep(200); } @SmallTest public void testSetPersistentVrThreadAPISuccess() throws Throwable { if (!mActivity.getPackageManager().hasSystemFeature( PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) { return; } int vr_thread = 0, policy = 0; vr_thread = Process.myTid(); setPersistentVrModeEnabled(true); mActivityManager.setPersistentVrThread(vr_thread); policy = (int) Process.getThreadScheduler(vr_thread); assertEquals((SCHED_FIFO | SCHED_RESET_ON_FORK), policy); // Check that the thread loses priority when persistent mode is disabled. setPersistentVrModeEnabled(false); policy = (int) Process.getThreadScheduler(vr_thread); assertEquals(SCHED_OTHER, policy); // Check that disabling VR mode when in persistent mode does not affect the persistent // thread. mActivity.setVrModeEnabled(true, mRequestedComponent); Thread.sleep(200); setPersistentVrModeEnabled(true); Thread.sleep(200); mActivityManager.setPersistentVrThread(vr_thread); policy = (int) Process.getThreadScheduler(vr_thread); assertEquals((SCHED_FIFO | SCHED_RESET_ON_FORK), policy); mActivity.setVrModeEnabled(false, mRequestedComponent); Thread.sleep(200); assertEquals((SCHED_FIFO | SCHED_RESET_ON_FORK), policy); setPersistentVrModeEnabled(false); policy = (int) Process.getThreadScheduler(vr_thread); assertEquals(SCHED_OTHER, policy); } @SmallTest public void testSetPersistentVrThreadAPIFailure() throws Throwable { if (!mActivity.getPackageManager().hasSystemFeature( PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) { return; } int vr_thread = 0, policy = 0; vr_thread = Process.myTid(); mActivityManager.setPersistentVrThread(vr_thread); policy = (int) Process.getThreadScheduler(vr_thread); assertEquals(SCHED_OTHER, policy); } @SmallTest public void testSetVrThreadAPIFailsInPersistentMode() throws Throwable { if (!mActivity.getPackageManager().hasSystemFeature( PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) { return; } int vr_thread = 0, policy = 0; mActivity.setVrModeEnabled(true, mRequestedComponent); vr_thread = Process.myTid(); setPersistentVrModeEnabled(true); mActivityManager.setVrThread(vr_thread); policy = (int) Process.getThreadScheduler(vr_thread); assertEquals(SCHED_OTHER, policy); setPersistentVrModeEnabled(false); // When not in persistent mode the API works again. mActivity.setVrModeEnabled(true, mRequestedComponent); mActivityManager.setVrThread(vr_thread); policy = (int) Process.getThreadScheduler(vr_thread); assertEquals((SCHED_FIFO | SCHED_RESET_ON_FORK), policy); // The thread loses priority when persistent mode is disabled. setPersistentVrModeEnabled(true); policy = (int) Process.getThreadScheduler(vr_thread); assertEquals(SCHED_OTHER, policy); setPersistentVrModeEnabled(false); } } services/core/java/com/android/server/am/ActivityManagerService.java +161 −37 Original line number Original line Diff line number Diff line Loading @@ -368,6 +368,7 @@ import com.android.server.firewall.IntentFirewall; import com.android.server.pm.Installer; import com.android.server.pm.Installer; import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.Installer.InstallerException; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.vr.PersistentVrStateListener; import com.android.server.vr.VrManagerInternal; import com.android.server.vr.VrManagerInternal; import com.android.server.wm.WindowManagerService; import com.android.server.wm.WindowManagerService; Loading Loading @@ -593,7 +594,70 @@ public class ActivityManagerService extends IActivityManager.Stub // default action automatically. Important for devices without direct input // default action automatically. Important for devices without direct input // devices. // devices. private boolean mShowDialogs = true; private boolean mShowDialogs = true; private boolean mInVrMode = false; // VR state flags. static final int NON_VR_MODE = 0; static final int VR_MODE = 1; static final int PERSISTENT_VR_MODE = 2; private int mVrState = NON_VR_MODE; private int mTopAppVrThreadTid = 0; private int mPersistentVrThreadTid = 0; final PersistentVrStateListener mPersistentVrModeListener = new PersistentVrStateListener() { @Override public void onPersistentVrStateChanged(boolean enabled) { synchronized(ActivityManagerService.this) { // There are 4 possible cases here: // // Cases for enabled == true // Invariant: mVrState != PERSISTENT_VR_MODE; // This is guaranteed as only this function sets mVrState to PERSISTENT_VR_MODE // Invariant: mPersistentVrThreadTid == 0 // This is guaranteed by VrManagerService, which only emits callbacks when the // mode changes, and in setPersistentVrThread, which only sets // mPersistentVrThreadTid when mVrState = PERSISTENT_VR_MODE // Case 1: mTopAppVrThreadTid > 0 (someone called setVrThread successfully and is // the top-app) // We reset the app which currently has SCHED_FIFO (mPersistentVrThreadTid) to // SCHED_OTHER // Case 2: mTopAppVrThreadTid == 0 // Do nothing // // Cases for enabled == false // Invariant: mVrState == PERSISTENT_VR_MODE; // This is guaranteed by VrManagerService, which only emits callbacks when the // mode changes, and the only other assignment of mVrState outside of this // function checks if mVrState != PERSISTENT_VR_MODE // Invariant: mTopAppVrThreadTid == 0 // This is guaranteed in that mTopAppVrThreadTid is only set to a tid when // mVrState is VR_MODE, and is explicitly set to 0 when setPersistentVrThread is // called // mPersistentVrThreadTid > 0 (someone called setPersistentVrThread successfully) // 3. Reset mPersistentVrThreadTidto SCHED_OTHER // mPersistentVrThreadTid == 0 // 4. Do nothing if (enabled) { mVrState = PERSISTENT_VR_MODE; } else { // Leaving persistent mode implies leaving VR mode. mVrState = NON_VR_MODE; } if (mVrState == PERSISTENT_VR_MODE) { if (mTopAppVrThreadTid > 0) { // Ensure that when entering persistent VR mode the last top-app loses // SCHED_FIFO. Process.setThreadScheduler(mTopAppVrThreadTid, Process.SCHED_OTHER, 0); mTopAppVrThreadTid = 0; } } else if (mPersistentVrThreadTid > 0) { // Ensure that when leaving persistent VR mode we reschedule the high priority // persistent thread. Process.setThreadScheduler(mPersistentVrThreadTid, Process.SCHED_OTHER, 0); mPersistentVrThreadTid = 0; } } } }; // Whether we should use SCHED_FIFO for UI and RenderThreads. // Whether we should use SCHED_FIFO for UI and RenderThreads. private boolean mUseFifoUiScheduling = false; private boolean mUseFifoUiScheduling = false; Loading Loading @@ -2325,28 +2389,36 @@ public class ActivityManagerService extends IActivityManager.Stub } } final ActivityRecord r = (ActivityRecord) msg.obj; final ActivityRecord r = (ActivityRecord) msg.obj; boolean vrMode; boolean vrMode; boolean inVrMode; ComponentName requestedPackage; ComponentName requestedPackage; ComponentName callingPackage; ComponentName callingPackage; int userId; int userId; synchronized (ActivityManagerService.this) { synchronized (ActivityManagerService.this) { vrMode = r.requestedVrComponent != null; vrMode = r.requestedVrComponent != null; inVrMode = mVrState != NON_VR_MODE; requestedPackage = r.requestedVrComponent; requestedPackage = r.requestedVrComponent; userId = r.userId; userId = r.userId; callingPackage = r.info.getComponentName(); callingPackage = r.info.getComponentName(); if (mInVrMode != vrMode) { if (vrMode != inVrMode) { mInVrMode = vrMode; // Don't change state if we're in persistent VR mode, but do update thread mShowDialogs = shouldShowDialogs(getGlobalConfiguration(), mInVrMode); // priorities if necessary. if (mVrState != PERSISTENT_VR_MODE) { mVrState = vrMode ? VR_MODE : NON_VR_MODE; } mShowDialogs = shouldShowDialogs(getGlobalConfiguration(), vrMode); if (r.app != null) { if (r.app != null) { ProcessRecord proc = r.app; ProcessRecord proc = r.app; if (proc.vrThreadTid > 0) { if (proc.vrThreadTid > 0) { if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) { if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) { try { try { if (mInVrMode == true) { if (mVrState == VR_MODE) { Process.setThreadScheduler(proc.vrThreadTid, Process.setThreadScheduler(proc.vrThreadTid, Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1); Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1); mTopAppVrThreadTid = proc.vrThreadTid; } else { } else { Process.setThreadScheduler(proc.vrThreadTid, Process.setThreadScheduler(proc.vrThreadTid, Process.SCHED_OTHER, 0); Process.SCHED_OTHER, 0); mTopAppVrThreadTid = 0; } } } catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) { Slog.w(TAG, "Failed to set scheduling policy, thread does" Slog.w(TAG, "Failed to set scheduling policy, thread does" Loading Loading @@ -13010,18 +13082,69 @@ public class ActivityManagerService extends IActivityManager.Stub } } } } @Override public void setVrThread(int tid) { public void setVrThread(int tid) { if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) { if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) { throw new UnsupportedOperationException("VR mode not supported on this device!"); throw new UnsupportedOperationException("VR mode not supported on this device!"); } } synchronized (this) { synchronized (this) { if (tid > 0 && mVrState == PERSISTENT_VR_MODE) { Slog.e(TAG, "VR thread cannot be set in persistent VR mode!"); return; } ProcessRecord proc; ProcessRecord proc; synchronized (mPidsSelfLocked) { synchronized (mPidsSelfLocked) { final int pid = Binder.getCallingPid(); final int pid = Binder.getCallingPid(); proc = mPidsSelfLocked.get(pid); proc = mPidsSelfLocked.get(pid); if (proc != null && mVrState == VR_MODE && tid >= 0) { proc.vrThreadTid = updateVrThreadLocked(proc, proc.vrThreadTid, pid, tid); mTopAppVrThreadTid = proc.vrThreadTid; } } } } @Override public void setPersistentVrThread(int tid) { if (checkCallingPermission(permission.RESTRICTED_VR_ACCESS) != PERMISSION_GRANTED) { String msg = "Permission Denial: setPersistentVrThread() from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() + " requires " + permission.RESTRICTED_VR_ACCESS; Slog.w(TAG, msg); throw new SecurityException(msg); } if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) { throw new UnsupportedOperationException("VR mode not supported on this device!"); } synchronized (this) { // Disable any existing VR thread. if (mTopAppVrThreadTid > 0) { Process.setThreadScheduler(mTopAppVrThreadTid, Process.SCHED_OTHER, 0); mTopAppVrThreadTid = 0; } if (tid > 0 && mVrState != PERSISTENT_VR_MODE) { Slog.e(TAG, "Persistent VR thread may only be set in persistent VR mode!"); return; } ProcessRecord proc; synchronized (mPidsSelfLocked) { final int pid = Binder.getCallingPid(); mPersistentVrThreadTid = updateVrThreadLocked(null, mPersistentVrThreadTid, pid, tid); } } } if (proc != null && mInVrMode && tid >= 0) { /** * Used by setVrThread and setPersistentVrThread to update a thread's priority. When proc is * non-null it must be in SCHED_GROUP_TOP_APP. When it is null, the tid is unconditionally * rescheduled. */ private int updateVrThreadLocked(ProcessRecord proc, int lastTid, int pid, int tid) { // ensure the tid belongs to the process // ensure the tid belongs to the process if (!Process.isThreadInProcess(pid, tid)) { if (!Process.isThreadInProcess(pid, tid)) { throw new IllegalArgumentException("VR thread does not belong to process"); throw new IllegalArgumentException("VR thread does not belong to process"); Loading @@ -13029,32 +13152,28 @@ public class ActivityManagerService extends IActivityManager.Stub // reset existing VR thread to CFS if this thread still exists and belongs to // reset existing VR thread to CFS if this thread still exists and belongs to // the calling process // the calling process if (proc.vrThreadTid != 0 if (lastTid != 0 && Process.isThreadInProcess(pid, lastTid)) { && Process.isThreadInProcess(pid, proc.vrThreadTid)) { try { try { Process.setThreadScheduler(proc.vrThreadTid, Process.SCHED_OTHER, 0); Process.setThreadScheduler(lastTid, Process.SCHED_OTHER, 0); } catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) { // Ignore this. Only occurs in race condition where previous VR thread // Ignore this. Only occurs in race condition where previous VR thread // was destroyed during this method call. // was destroyed during this method call. } } } } proc.vrThreadTid = tid; // promote to FIFO now if the tid is non-zero // promote to FIFO now if the tid is non-zero try { try { if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP && if ((proc == null || proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) proc.vrThreadTid > 0) { && tid > 0) { Process.setThreadScheduler(proc.vrThreadTid, Process.setThreadScheduler(tid, Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1); Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1); } } return tid; } catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) { Slog.e(TAG, "Failed to set scheduling policy, thread does" Slog.e(TAG, "Failed to set scheduling policy, thread does" + " not exist:\n" + e); + " not exist:\n" + e); } } } return lastTid; } } } } @Override @Override Loading Loading @@ -13643,7 +13762,10 @@ public class ActivityManagerService extends IActivityManager.Stub mLocalDeviceIdleController mLocalDeviceIdleController = LocalServices.getService(DeviceIdleController.LocalService.class); = LocalServices.getService(DeviceIdleController.LocalService.class); mAssistUtils = new AssistUtils(mContext); mAssistUtils = new AssistUtils(mContext); VrManagerInternal vrManagerInternal = LocalServices.getService(VrManagerInternal.class); if (vrManagerInternal != null) { vrManagerInternal.addPersistentVrModeStateListener(mPersistentVrModeListener); } // Make sure we have the current profile info, since it is needed for security checks. // Make sure we have the current profile info, since it is needed for security checks. mUserController.onSystemReady(); mUserController.onSystemReady(); mRecentTasks.onSystemReadyLocked(); mRecentTasks.onSystemReadyLocked(); Loading Loading @@ -19740,7 +19862,7 @@ public class ActivityManagerService extends IActivityManager.Stub mUserController.getCurrentUserIdLocked()); mUserController.getCurrentUserIdLocked()); // TODO: If our config changes, should we auto dismiss any currently showing dialogs? // TODO: If our config changes, should we auto dismiss any currently showing dialogs? mShowDialogs = shouldShowDialogs(mTempConfig, mInVrMode); mShowDialogs = shouldShowDialogs(mTempConfig, mVrState != NON_VR_MODE); AttributeCache ac = AttributeCache.instance(); AttributeCache ac = AttributeCache.instance(); if (ac != null) { if (ac != null) { Loading Loading @@ -21281,10 +21403,11 @@ public class ActivityManagerService extends IActivityManager.Stub // do nothing if we already switched to RT // do nothing if we already switched to RT if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) { if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) { // Switch VR thread for app to SCHED_FIFO // Switch VR thread for app to SCHED_FIFO if (mInVrMode && app.vrThreadTid != 0) { if (mVrState == VR_MODE && app.vrThreadTid != 0) { try { try { Process.setThreadScheduler(app.vrThreadTid, Process.setThreadScheduler(app.vrThreadTid, Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1); Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1); mTopAppVrThreadTid = app.vrThreadTid; } catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) { // thread died, ignore // thread died, ignore } } Loading Loading @@ -21332,6 +21455,7 @@ public class ActivityManagerService extends IActivityManager.Stub // Safe to do even if we're not in VR mode // Safe to do even if we're not in VR mode if (app.vrThreadTid != 0) { if (app.vrThreadTid != 0) { Process.setThreadScheduler(app.vrThreadTid, Process.SCHED_OTHER, 0); Process.setThreadScheduler(app.vrThreadTid, Process.SCHED_OTHER, 0); mTopAppVrThreadTid = 0; } } if (mUseFifoUiScheduling) { if (mUseFifoUiScheduling) { // Reset UI pipeline to SCHED_OTHER // Reset UI pipeline to SCHED_OTHER Loading
core/java/android/app/ActivityManager.java +29 −0 Original line number Original line Diff line number Diff line Loading @@ -4060,6 +4060,10 @@ public class ActivityManager { * thread can be a VR thread in a process at a time, and that thread may be subject to * thread can be a VR thread in a process at a time, and that thread may be subject to * restrictions on the amount of time it can run. * restrictions on the amount of time it can run. * * * If persistent VR mode is set, whatever thread has been granted aggressive scheduling via this * method will return to normal operation, and calling this method will do nothing while * persistent VR mode is enabled. * * To reset the VR thread for an application, a tid of 0 can be passed. * To reset the VR thread for an application, a tid of 0 can be passed. * * * @see android.os.Process#myTid() * @see android.os.Process#myTid() Loading @@ -4073,6 +4077,31 @@ public class ActivityManager { } } } } /** * Enable more aggressive scheduling for latency-sensitive low-runtime VR threads that persist * beyond a single process. It requires holding the * {@link android.Manifest.permission#RESTRICTED_VR_ACCESS} permission. Only one thread can be a * persistent VR thread at a time, and that thread may be subject to restrictions on the amount * of time it can run. Calling this method will disable aggressive scheduling for non-persistent * VR threads set via {@link #setVrThread}. If persistent VR mode is disabled then the * persistent VR thread loses its new scheduling priority; this method must be called again to * set the persistent thread. * * To reset the persistent VR thread, a tid of 0 can be passed. * * @see android.os.Process#myTid() * @param tid tid of the VR thread * @hide */ @RequiresPermission(Manifest.permission.RESTRICTED_VR_ACCESS) public static void setPersistentVrThread(int tid) { try { getService().setPersistentVrThread(tid); } catch (RemoteException e) { // pass } } /** /** * The AppTask allows you to manage your own application's tasks. * The AppTask allows you to manage your own application's tasks. * See {@link android.app.ActivityManager#getAppTasks()} * See {@link android.app.ActivityManager#getAppTasks()} Loading
core/java/android/app/IActivityManager.aidl +1 −0 Original line number Original line Diff line number Diff line Loading @@ -605,6 +605,7 @@ interface IActivityManager { ActivityManager.TaskSnapshot getTaskSnapshot(int taskId); ActivityManager.TaskSnapshot getTaskSnapshot(int taskId); void scheduleApplicationInfoChanged(in List<String> packageNames, int userId); void scheduleApplicationInfoChanged(in List<String> packageNames, int userId); void setPersistentVrThread(int tid); // WARNING: when these transactions are updated, check if they are any callers on the native // WARNING: when these transactions are updated, check if they are any callers on the native // side. If so, make sure they are using the correct transaction ids and arguments. // side. If so, make sure they are using the correct transaction ids and arguments. Loading
core/tests/coretests/AndroidManifest.xml +3 −0 Original line number Original line Diff line number Diff line Loading @@ -115,6 +115,9 @@ <!-- accessibility test permissions --> <!-- accessibility test permissions --> <uses-permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT" /> <uses-permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT" /> <!-- vr test permissions --> <uses-permission android:name="android.permission.RESTRICTED_VR_ACCESS" /> <application android:theme="@style/Theme" android:supportsRtl="true"> <application android:theme="@style/Theme" android:supportsRtl="true"> <uses-library android:name="android.test.runner" /> <uses-library android:name="android.test.runner" /> <uses-library android:name="org.apache.http.legacy" android:required="false" /> <uses-library android:name="org.apache.http.legacy" android:required="false" /> Loading
core/tests/coretests/src/android/os/SetPersistentVrThreadTest.java 0 → 100644 +162 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2017 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 android.os; import android.app.ActivityManager; import android.app.VrManager; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; import android.os.Process; import android.provider.Settings; import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.SmallTest; import android.util.Log; /** * Tests ActivityManager#setPersistentVrThread and ActivityManager#setVrThread's * interaction with persistent VR mode. */ public class SetPersistentVrThreadTest extends ActivityInstrumentationTestCase2<TestVrActivity> { private TestVrActivity mActivity; private ActivityManager mActivityManager; private VrManager mVrManager; private Context mContext; private String mOldVrListener; private ComponentName mRequestedComponent; private static final int SCHED_OTHER = 0; private static final int SCHED_FIFO = 1; private static final int SCHED_RESET_ON_FORK = 0x40000000; public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners"; private static final String TAG = "VrSetPersistentFIFOThreadTest"; public SetPersistentVrThreadTest() { super(TestVrActivity.class); } @Override protected void setUp() throws Exception { super.setUp(); mContext = getInstrumentation().getTargetContext(); mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); mVrManager = (VrManager) mContext.getSystemService(Context.VR_SERVICE); mRequestedComponent = new ComponentName(mContext, TestVrActivity.TestVrListenerService.class); mOldVrListener = Settings.Secure.getString(mContext.getContentResolver(), ENABLED_VR_LISTENERS); Settings.Secure.putString(mContext.getContentResolver(), ENABLED_VR_LISTENERS, mRequestedComponent.flattenToString()); mActivity = getActivity(); } @Override protected void tearDown() throws Exception { try { setPersistentVrModeEnabled(false); } catch (Throwable e) { // pass } Settings.Secure.putString(mContext.getContentResolver(), ENABLED_VR_LISTENERS, mOldVrListener); super.tearDown(); } private void setPersistentVrModeEnabled(boolean enable) throws Throwable { mVrManager.setPersistentVrModeEnabled(enable); // Allow the system time to send out callbacks for persistent VR mode. Thread.sleep(200); } @SmallTest public void testSetPersistentVrThreadAPISuccess() throws Throwable { if (!mActivity.getPackageManager().hasSystemFeature( PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) { return; } int vr_thread = 0, policy = 0; vr_thread = Process.myTid(); setPersistentVrModeEnabled(true); mActivityManager.setPersistentVrThread(vr_thread); policy = (int) Process.getThreadScheduler(vr_thread); assertEquals((SCHED_FIFO | SCHED_RESET_ON_FORK), policy); // Check that the thread loses priority when persistent mode is disabled. setPersistentVrModeEnabled(false); policy = (int) Process.getThreadScheduler(vr_thread); assertEquals(SCHED_OTHER, policy); // Check that disabling VR mode when in persistent mode does not affect the persistent // thread. mActivity.setVrModeEnabled(true, mRequestedComponent); Thread.sleep(200); setPersistentVrModeEnabled(true); Thread.sleep(200); mActivityManager.setPersistentVrThread(vr_thread); policy = (int) Process.getThreadScheduler(vr_thread); assertEquals((SCHED_FIFO | SCHED_RESET_ON_FORK), policy); mActivity.setVrModeEnabled(false, mRequestedComponent); Thread.sleep(200); assertEquals((SCHED_FIFO | SCHED_RESET_ON_FORK), policy); setPersistentVrModeEnabled(false); policy = (int) Process.getThreadScheduler(vr_thread); assertEquals(SCHED_OTHER, policy); } @SmallTest public void testSetPersistentVrThreadAPIFailure() throws Throwable { if (!mActivity.getPackageManager().hasSystemFeature( PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) { return; } int vr_thread = 0, policy = 0; vr_thread = Process.myTid(); mActivityManager.setPersistentVrThread(vr_thread); policy = (int) Process.getThreadScheduler(vr_thread); assertEquals(SCHED_OTHER, policy); } @SmallTest public void testSetVrThreadAPIFailsInPersistentMode() throws Throwable { if (!mActivity.getPackageManager().hasSystemFeature( PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) { return; } int vr_thread = 0, policy = 0; mActivity.setVrModeEnabled(true, mRequestedComponent); vr_thread = Process.myTid(); setPersistentVrModeEnabled(true); mActivityManager.setVrThread(vr_thread); policy = (int) Process.getThreadScheduler(vr_thread); assertEquals(SCHED_OTHER, policy); setPersistentVrModeEnabled(false); // When not in persistent mode the API works again. mActivity.setVrModeEnabled(true, mRequestedComponent); mActivityManager.setVrThread(vr_thread); policy = (int) Process.getThreadScheduler(vr_thread); assertEquals((SCHED_FIFO | SCHED_RESET_ON_FORK), policy); // The thread loses priority when persistent mode is disabled. setPersistentVrModeEnabled(true); policy = (int) Process.getThreadScheduler(vr_thread); assertEquals(SCHED_OTHER, policy); setPersistentVrModeEnabled(false); } }
services/core/java/com/android/server/am/ActivityManagerService.java +161 −37 Original line number Original line Diff line number Diff line Loading @@ -368,6 +368,7 @@ import com.android.server.firewall.IntentFirewall; import com.android.server.pm.Installer; import com.android.server.pm.Installer; import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.Installer.InstallerException; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.vr.PersistentVrStateListener; import com.android.server.vr.VrManagerInternal; import com.android.server.vr.VrManagerInternal; import com.android.server.wm.WindowManagerService; import com.android.server.wm.WindowManagerService; Loading Loading @@ -593,7 +594,70 @@ public class ActivityManagerService extends IActivityManager.Stub // default action automatically. Important for devices without direct input // default action automatically. Important for devices without direct input // devices. // devices. private boolean mShowDialogs = true; private boolean mShowDialogs = true; private boolean mInVrMode = false; // VR state flags. static final int NON_VR_MODE = 0; static final int VR_MODE = 1; static final int PERSISTENT_VR_MODE = 2; private int mVrState = NON_VR_MODE; private int mTopAppVrThreadTid = 0; private int mPersistentVrThreadTid = 0; final PersistentVrStateListener mPersistentVrModeListener = new PersistentVrStateListener() { @Override public void onPersistentVrStateChanged(boolean enabled) { synchronized(ActivityManagerService.this) { // There are 4 possible cases here: // // Cases for enabled == true // Invariant: mVrState != PERSISTENT_VR_MODE; // This is guaranteed as only this function sets mVrState to PERSISTENT_VR_MODE // Invariant: mPersistentVrThreadTid == 0 // This is guaranteed by VrManagerService, which only emits callbacks when the // mode changes, and in setPersistentVrThread, which only sets // mPersistentVrThreadTid when mVrState = PERSISTENT_VR_MODE // Case 1: mTopAppVrThreadTid > 0 (someone called setVrThread successfully and is // the top-app) // We reset the app which currently has SCHED_FIFO (mPersistentVrThreadTid) to // SCHED_OTHER // Case 2: mTopAppVrThreadTid == 0 // Do nothing // // Cases for enabled == false // Invariant: mVrState == PERSISTENT_VR_MODE; // This is guaranteed by VrManagerService, which only emits callbacks when the // mode changes, and the only other assignment of mVrState outside of this // function checks if mVrState != PERSISTENT_VR_MODE // Invariant: mTopAppVrThreadTid == 0 // This is guaranteed in that mTopAppVrThreadTid is only set to a tid when // mVrState is VR_MODE, and is explicitly set to 0 when setPersistentVrThread is // called // mPersistentVrThreadTid > 0 (someone called setPersistentVrThread successfully) // 3. Reset mPersistentVrThreadTidto SCHED_OTHER // mPersistentVrThreadTid == 0 // 4. Do nothing if (enabled) { mVrState = PERSISTENT_VR_MODE; } else { // Leaving persistent mode implies leaving VR mode. mVrState = NON_VR_MODE; } if (mVrState == PERSISTENT_VR_MODE) { if (mTopAppVrThreadTid > 0) { // Ensure that when entering persistent VR mode the last top-app loses // SCHED_FIFO. Process.setThreadScheduler(mTopAppVrThreadTid, Process.SCHED_OTHER, 0); mTopAppVrThreadTid = 0; } } else if (mPersistentVrThreadTid > 0) { // Ensure that when leaving persistent VR mode we reschedule the high priority // persistent thread. Process.setThreadScheduler(mPersistentVrThreadTid, Process.SCHED_OTHER, 0); mPersistentVrThreadTid = 0; } } } }; // Whether we should use SCHED_FIFO for UI and RenderThreads. // Whether we should use SCHED_FIFO for UI and RenderThreads. private boolean mUseFifoUiScheduling = false; private boolean mUseFifoUiScheduling = false; Loading Loading @@ -2325,28 +2389,36 @@ public class ActivityManagerService extends IActivityManager.Stub } } final ActivityRecord r = (ActivityRecord) msg.obj; final ActivityRecord r = (ActivityRecord) msg.obj; boolean vrMode; boolean vrMode; boolean inVrMode; ComponentName requestedPackage; ComponentName requestedPackage; ComponentName callingPackage; ComponentName callingPackage; int userId; int userId; synchronized (ActivityManagerService.this) { synchronized (ActivityManagerService.this) { vrMode = r.requestedVrComponent != null; vrMode = r.requestedVrComponent != null; inVrMode = mVrState != NON_VR_MODE; requestedPackage = r.requestedVrComponent; requestedPackage = r.requestedVrComponent; userId = r.userId; userId = r.userId; callingPackage = r.info.getComponentName(); callingPackage = r.info.getComponentName(); if (mInVrMode != vrMode) { if (vrMode != inVrMode) { mInVrMode = vrMode; // Don't change state if we're in persistent VR mode, but do update thread mShowDialogs = shouldShowDialogs(getGlobalConfiguration(), mInVrMode); // priorities if necessary. if (mVrState != PERSISTENT_VR_MODE) { mVrState = vrMode ? VR_MODE : NON_VR_MODE; } mShowDialogs = shouldShowDialogs(getGlobalConfiguration(), vrMode); if (r.app != null) { if (r.app != null) { ProcessRecord proc = r.app; ProcessRecord proc = r.app; if (proc.vrThreadTid > 0) { if (proc.vrThreadTid > 0) { if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) { if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) { try { try { if (mInVrMode == true) { if (mVrState == VR_MODE) { Process.setThreadScheduler(proc.vrThreadTid, Process.setThreadScheduler(proc.vrThreadTid, Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1); Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1); mTopAppVrThreadTid = proc.vrThreadTid; } else { } else { Process.setThreadScheduler(proc.vrThreadTid, Process.setThreadScheduler(proc.vrThreadTid, Process.SCHED_OTHER, 0); Process.SCHED_OTHER, 0); mTopAppVrThreadTid = 0; } } } catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) { Slog.w(TAG, "Failed to set scheduling policy, thread does" Slog.w(TAG, "Failed to set scheduling policy, thread does" Loading Loading @@ -13010,18 +13082,69 @@ public class ActivityManagerService extends IActivityManager.Stub } } } } @Override public void setVrThread(int tid) { public void setVrThread(int tid) { if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) { if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) { throw new UnsupportedOperationException("VR mode not supported on this device!"); throw new UnsupportedOperationException("VR mode not supported on this device!"); } } synchronized (this) { synchronized (this) { if (tid > 0 && mVrState == PERSISTENT_VR_MODE) { Slog.e(TAG, "VR thread cannot be set in persistent VR mode!"); return; } ProcessRecord proc; ProcessRecord proc; synchronized (mPidsSelfLocked) { synchronized (mPidsSelfLocked) { final int pid = Binder.getCallingPid(); final int pid = Binder.getCallingPid(); proc = mPidsSelfLocked.get(pid); proc = mPidsSelfLocked.get(pid); if (proc != null && mVrState == VR_MODE && tid >= 0) { proc.vrThreadTid = updateVrThreadLocked(proc, proc.vrThreadTid, pid, tid); mTopAppVrThreadTid = proc.vrThreadTid; } } } } @Override public void setPersistentVrThread(int tid) { if (checkCallingPermission(permission.RESTRICTED_VR_ACCESS) != PERMISSION_GRANTED) { String msg = "Permission Denial: setPersistentVrThread() from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() + " requires " + permission.RESTRICTED_VR_ACCESS; Slog.w(TAG, msg); throw new SecurityException(msg); } if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) { throw new UnsupportedOperationException("VR mode not supported on this device!"); } synchronized (this) { // Disable any existing VR thread. if (mTopAppVrThreadTid > 0) { Process.setThreadScheduler(mTopAppVrThreadTid, Process.SCHED_OTHER, 0); mTopAppVrThreadTid = 0; } if (tid > 0 && mVrState != PERSISTENT_VR_MODE) { Slog.e(TAG, "Persistent VR thread may only be set in persistent VR mode!"); return; } ProcessRecord proc; synchronized (mPidsSelfLocked) { final int pid = Binder.getCallingPid(); mPersistentVrThreadTid = updateVrThreadLocked(null, mPersistentVrThreadTid, pid, tid); } } } if (proc != null && mInVrMode && tid >= 0) { /** * Used by setVrThread and setPersistentVrThread to update a thread's priority. When proc is * non-null it must be in SCHED_GROUP_TOP_APP. When it is null, the tid is unconditionally * rescheduled. */ private int updateVrThreadLocked(ProcessRecord proc, int lastTid, int pid, int tid) { // ensure the tid belongs to the process // ensure the tid belongs to the process if (!Process.isThreadInProcess(pid, tid)) { if (!Process.isThreadInProcess(pid, tid)) { throw new IllegalArgumentException("VR thread does not belong to process"); throw new IllegalArgumentException("VR thread does not belong to process"); Loading @@ -13029,32 +13152,28 @@ public class ActivityManagerService extends IActivityManager.Stub // reset existing VR thread to CFS if this thread still exists and belongs to // reset existing VR thread to CFS if this thread still exists and belongs to // the calling process // the calling process if (proc.vrThreadTid != 0 if (lastTid != 0 && Process.isThreadInProcess(pid, lastTid)) { && Process.isThreadInProcess(pid, proc.vrThreadTid)) { try { try { Process.setThreadScheduler(proc.vrThreadTid, Process.SCHED_OTHER, 0); Process.setThreadScheduler(lastTid, Process.SCHED_OTHER, 0); } catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) { // Ignore this. Only occurs in race condition where previous VR thread // Ignore this. Only occurs in race condition where previous VR thread // was destroyed during this method call. // was destroyed during this method call. } } } } proc.vrThreadTid = tid; // promote to FIFO now if the tid is non-zero // promote to FIFO now if the tid is non-zero try { try { if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP && if ((proc == null || proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) proc.vrThreadTid > 0) { && tid > 0) { Process.setThreadScheduler(proc.vrThreadTid, Process.setThreadScheduler(tid, Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1); Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1); } } return tid; } catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) { Slog.e(TAG, "Failed to set scheduling policy, thread does" Slog.e(TAG, "Failed to set scheduling policy, thread does" + " not exist:\n" + e); + " not exist:\n" + e); } } } return lastTid; } } } } @Override @Override Loading Loading @@ -13643,7 +13762,10 @@ public class ActivityManagerService extends IActivityManager.Stub mLocalDeviceIdleController mLocalDeviceIdleController = LocalServices.getService(DeviceIdleController.LocalService.class); = LocalServices.getService(DeviceIdleController.LocalService.class); mAssistUtils = new AssistUtils(mContext); mAssistUtils = new AssistUtils(mContext); VrManagerInternal vrManagerInternal = LocalServices.getService(VrManagerInternal.class); if (vrManagerInternal != null) { vrManagerInternal.addPersistentVrModeStateListener(mPersistentVrModeListener); } // Make sure we have the current profile info, since it is needed for security checks. // Make sure we have the current profile info, since it is needed for security checks. mUserController.onSystemReady(); mUserController.onSystemReady(); mRecentTasks.onSystemReadyLocked(); mRecentTasks.onSystemReadyLocked(); Loading Loading @@ -19740,7 +19862,7 @@ public class ActivityManagerService extends IActivityManager.Stub mUserController.getCurrentUserIdLocked()); mUserController.getCurrentUserIdLocked()); // TODO: If our config changes, should we auto dismiss any currently showing dialogs? // TODO: If our config changes, should we auto dismiss any currently showing dialogs? mShowDialogs = shouldShowDialogs(mTempConfig, mInVrMode); mShowDialogs = shouldShowDialogs(mTempConfig, mVrState != NON_VR_MODE); AttributeCache ac = AttributeCache.instance(); AttributeCache ac = AttributeCache.instance(); if (ac != null) { if (ac != null) { Loading Loading @@ -21281,10 +21403,11 @@ public class ActivityManagerService extends IActivityManager.Stub // do nothing if we already switched to RT // do nothing if we already switched to RT if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) { if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) { // Switch VR thread for app to SCHED_FIFO // Switch VR thread for app to SCHED_FIFO if (mInVrMode && app.vrThreadTid != 0) { if (mVrState == VR_MODE && app.vrThreadTid != 0) { try { try { Process.setThreadScheduler(app.vrThreadTid, Process.setThreadScheduler(app.vrThreadTid, Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1); Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1); mTopAppVrThreadTid = app.vrThreadTid; } catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) { // thread died, ignore // thread died, ignore } } Loading Loading @@ -21332,6 +21455,7 @@ public class ActivityManagerService extends IActivityManager.Stub // Safe to do even if we're not in VR mode // Safe to do even if we're not in VR mode if (app.vrThreadTid != 0) { if (app.vrThreadTid != 0) { Process.setThreadScheduler(app.vrThreadTid, Process.SCHED_OTHER, 0); Process.setThreadScheduler(app.vrThreadTid, Process.SCHED_OTHER, 0); mTopAppVrThreadTid = 0; } } if (mUseFifoUiScheduling) { if (mUseFifoUiScheduling) { // Reset UI pipeline to SCHED_OTHER // Reset UI pipeline to SCHED_OTHER