Loading core/api/system-current.txt +6 −0 Original line number Diff line number Diff line Loading @@ -11199,6 +11199,7 @@ package android.service.games { method public void onTransientSystemBarVisibilityFromRevealGestureChanged(boolean); method @RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY) public final boolean restartGame(); method public void setTaskOverlayView(@NonNull android.view.View, @NonNull android.view.ViewGroup.LayoutParams); method @RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY) public final void startActivityFromGameSessionForResult(@NonNull android.content.Intent, @Nullable android.os.Bundle, @NonNull java.util.concurrent.Executor, @NonNull android.service.games.GameSessionActivityCallback); method public void takeScreenshot(@NonNull java.util.concurrent.Executor, @NonNull android.service.games.GameSession.ScreenshotCallback); } Loading @@ -11208,6 +11209,11 @@ package android.service.games { field public static final int ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR = 0; // 0x0 } public interface GameSessionActivityCallback { method public void onActivityResult(int, @Nullable android.content.Intent); method public default void onActivityStartFailed(@NonNull Throwable); } public abstract class GameSessionService extends android.app.Service { ctor public GameSessionService(); method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent); core/java/android/app/Activity.java +14 −3 Original line number Diff line number Diff line Loading @@ -5503,6 +5503,17 @@ public class Activity extends ContextThemeWrapper */ public void startActivityAsCaller(Intent intent, @Nullable Bundle options, IBinder permissionToken, boolean ignoreTargetSecurity, int userId) { startActivityAsCaller(intent, options, permissionToken, ignoreTargetSecurity, userId, -1); } /** * @see #startActivityAsCaller(Intent, Bundle, IBinder, boolean, int) * @param requestCode The request code used for returning a result or -1 if no result should be * returned. * @hide */ public void startActivityAsCaller(Intent intent, @Nullable Bundle options, IBinder permissionToken, boolean ignoreTargetSecurity, int userId, int requestCode) { if (mParent != null) { throw new RuntimeException("Can't be called from a child"); } Loading @@ -5510,11 +5521,11 @@ public class Activity extends ContextThemeWrapper Instrumentation.ActivityResult ar = mInstrumentation.execStartActivityAsCaller( this, mMainThread.getApplicationThread(), mToken, this, intent, -1, options, permissionToken, ignoreTargetSecurity, userId); intent, requestCode, options, permissionToken, ignoreTargetSecurity, userId); if (ar != null) { mMainThread.sendActivityResult( mToken, mEmbeddedID, -1, ar.getResultCode(), ar.getResultData()); mToken, mEmbeddedID, requestCode, ar.getResultCode(), ar.getResultData()); } cancelInputsAndStartExitTransition(options); } Loading core/java/android/app/IActivityTaskManager.aidl +3 −0 Original line number Diff line number Diff line Loading @@ -130,6 +130,9 @@ interface IActivityTaskManager { in ProfilerInfo profilerInfo, in Bundle options, int userId); int startAssistantActivity(in String callingPackage, in String callingFeatureId, int callingPid, int callingUid, in Intent intent, in String resolvedType, in Bundle options, int userId); int startActivityFromGameSession(IApplicationThread caller, in String callingPackage, in String callingFeatureId, int callingPid, int callingUid, in Intent intent, int taskId, int userId); void startRecentsActivity(in Intent intent, in long eventTime, in IRecentsAnimationRunner recentsAnimationRunner); int startActivityFromRecents(int taskId, in Bundle options); Loading core/java/android/service/games/GameSession.java +74 −0 Original line number Diff line number Diff line Loading @@ -20,15 +20,23 @@ import android.annotation.Hide; import android.annotation.IntDef; import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.app.ActivityTaskManager; import android.app.Instrumentation; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.Rect; import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; import android.util.Slog; import android.view.SurfaceControlViewHost; import android.view.View; Loading @@ -41,6 +49,7 @@ import com.android.internal.util.function.pooled.PooledLambda; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; import java.util.concurrent.Executor; /** Loading Loading @@ -120,6 +129,7 @@ public abstract class GameSession { private LifecycleState mLifecycleState = LifecycleState.INITIALIZED; private boolean mAreTransientInsetsVisibleDueToGesture = false; private IGameSessionController mGameSessionController; private Context mContext; private int mTaskId; private GameSessionRootView mGameSessionRootView; private SurfaceControlViewHost mSurfaceControlViewHost; Loading @@ -137,6 +147,7 @@ public abstract class GameSession { int heightPx) { mGameSessionController = gameSessionController; mTaskId = taskId; mContext = context; mSurfaceControlViewHost = surfaceControlViewHost; mGameSessionRootView = new GameSessionRootView(context, mSurfaceControlViewHost); surfaceControlViewHost.setView(mGameSessionRootView, widthPx, heightPx); Loading Loading @@ -458,4 +469,67 @@ public abstract class GameSession { break; } } /** * Launches an activity within the same activity stack as the {@link GameSession}. When the * target activity exits, {@link GameSessionActivityCallback#onActivityResult(int, Intent)} will * be invoked with the result code and result data directly from the target activity (in other * words, the result code and data set via the target activity's * {@link android.app.Activity#startActivityForResult} call). The caller is expected to handle * the results that the target activity returns. * * <p>Any activity that an app would normally be able to start via {@link * android.app.Activity#startActivityForResult} will be startable via this method. * * <p>Started activities may see a different calling package than the game session's package * when calling {@link android.app.Activity#getCallingPackage()}. * * <p> If an exception is thrown while handling {@code intent}, * {@link GameSessionActivityCallback#onActivityStartFailed(Throwable)} will be called instead * of {@link GameSessionActivityCallback#onActivityResult(int, Intent)}. * * @param intent The intent to start. * @param options Additional options for how the Activity should be started. See * {@link android.app.Activity#startActivityForResult(Intent, int, Bundle)} for * more details. This value may be null. * @param executor Executor on which {@code callback} should be invoked. * @param callback Callback to be invoked once the started activity has finished. */ @RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY) public final void startActivityFromGameSessionForResult( @NonNull Intent intent, @Nullable Bundle options, @NonNull Executor executor, @NonNull GameSessionActivityCallback callback) { Objects.requireNonNull(intent); Objects.requireNonNull(executor); Objects.requireNonNull(callback); AndroidFuture<GameSessionActivityResult> future = new AndroidFuture<GameSessionActivityResult>() .whenCompleteAsync((result, ex) -> { if (ex != null) { callback.onActivityStartFailed(ex); return; } callback.onActivityResult(result.getResultCode(), result.getData()); }, executor); final Intent trampolineIntent = new Intent(); trampolineIntent.setComponent( new ComponentName( "android", "android.service.games.GameSessionTrampolineActivity")); trampolineIntent.putExtra(GameSessionTrampolineActivity.INTENT_KEY, intent); trampolineIntent.putExtra(GameSessionTrampolineActivity.OPTIONS_KEY, options); trampolineIntent.putExtra( GameSessionTrampolineActivity.FUTURE_KEY, future); try { int result = ActivityTaskManager.getService().startActivityFromGameSession( mContext.getIApplicationThread(), mContext.getPackageName(), "GameSession", Binder.getCallingPid(), Binder.getCallingUid(), trampolineIntent, mTaskId, UserHandle.myUserId()); Instrumentation.checkStartActivityResult(result, trampolineIntent); } catch (Throwable t) { executor.execute(() -> callback.onActivityStartFailed(t)); } } } core/java/android/service/games/GameSessionActivityCallback.java 0 → 100644 +54 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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.service.games; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.content.Intent; import android.os.Bundle; import java.util.concurrent.Executor; /** * Callback invoked when an activity launched via * {@link GameSession#startActivityFromGameSessionForResult(Intent, Bundle, Executor, * GameSessionActivityCallback)}} has returned a result or failed to start. * * @hide */ @SystemApi public interface GameSessionActivityCallback { /** * Callback invoked when an activity launched via * {@link GameSession#startActivityFromGameSessionForResult(Intent, Bundle, Executor, * GameSessionActivityCallback)}} has returned a result. * * @param resultCode The result code of the launched activity. See {@link * android.app.Activity#setResult(int)}. * @param data Any data returned by the launched activity. See {@link * android.app.Activity#setResult(int, Intent)}. */ void onActivityResult(int resultCode, @Nullable Intent data); /** * Callback invoked when a throwable was thrown when launching the {@link Intent} in * {@link GameSession#startActivityFromGameSessionForResult(Intent, Bundle, Executor, * GameSessionActivityCallback)}}. */ default void onActivityStartFailed(@NonNull Throwable t) {} } Loading
core/api/system-current.txt +6 −0 Original line number Diff line number Diff line Loading @@ -11199,6 +11199,7 @@ package android.service.games { method public void onTransientSystemBarVisibilityFromRevealGestureChanged(boolean); method @RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY) public final boolean restartGame(); method public void setTaskOverlayView(@NonNull android.view.View, @NonNull android.view.ViewGroup.LayoutParams); method @RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY) public final void startActivityFromGameSessionForResult(@NonNull android.content.Intent, @Nullable android.os.Bundle, @NonNull java.util.concurrent.Executor, @NonNull android.service.games.GameSessionActivityCallback); method public void takeScreenshot(@NonNull java.util.concurrent.Executor, @NonNull android.service.games.GameSession.ScreenshotCallback); } Loading @@ -11208,6 +11209,11 @@ package android.service.games { field public static final int ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR = 0; // 0x0 } public interface GameSessionActivityCallback { method public void onActivityResult(int, @Nullable android.content.Intent); method public default void onActivityStartFailed(@NonNull Throwable); } public abstract class GameSessionService extends android.app.Service { ctor public GameSessionService(); method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent);
core/java/android/app/Activity.java +14 −3 Original line number Diff line number Diff line Loading @@ -5503,6 +5503,17 @@ public class Activity extends ContextThemeWrapper */ public void startActivityAsCaller(Intent intent, @Nullable Bundle options, IBinder permissionToken, boolean ignoreTargetSecurity, int userId) { startActivityAsCaller(intent, options, permissionToken, ignoreTargetSecurity, userId, -1); } /** * @see #startActivityAsCaller(Intent, Bundle, IBinder, boolean, int) * @param requestCode The request code used for returning a result or -1 if no result should be * returned. * @hide */ public void startActivityAsCaller(Intent intent, @Nullable Bundle options, IBinder permissionToken, boolean ignoreTargetSecurity, int userId, int requestCode) { if (mParent != null) { throw new RuntimeException("Can't be called from a child"); } Loading @@ -5510,11 +5521,11 @@ public class Activity extends ContextThemeWrapper Instrumentation.ActivityResult ar = mInstrumentation.execStartActivityAsCaller( this, mMainThread.getApplicationThread(), mToken, this, intent, -1, options, permissionToken, ignoreTargetSecurity, userId); intent, requestCode, options, permissionToken, ignoreTargetSecurity, userId); if (ar != null) { mMainThread.sendActivityResult( mToken, mEmbeddedID, -1, ar.getResultCode(), ar.getResultData()); mToken, mEmbeddedID, requestCode, ar.getResultCode(), ar.getResultData()); } cancelInputsAndStartExitTransition(options); } Loading
core/java/android/app/IActivityTaskManager.aidl +3 −0 Original line number Diff line number Diff line Loading @@ -130,6 +130,9 @@ interface IActivityTaskManager { in ProfilerInfo profilerInfo, in Bundle options, int userId); int startAssistantActivity(in String callingPackage, in String callingFeatureId, int callingPid, int callingUid, in Intent intent, in String resolvedType, in Bundle options, int userId); int startActivityFromGameSession(IApplicationThread caller, in String callingPackage, in String callingFeatureId, int callingPid, int callingUid, in Intent intent, int taskId, int userId); void startRecentsActivity(in Intent intent, in long eventTime, in IRecentsAnimationRunner recentsAnimationRunner); int startActivityFromRecents(int taskId, in Bundle options); Loading
core/java/android/service/games/GameSession.java +74 −0 Original line number Diff line number Diff line Loading @@ -20,15 +20,23 @@ import android.annotation.Hide; import android.annotation.IntDef; import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.app.ActivityTaskManager; import android.app.Instrumentation; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.Rect; import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; import android.util.Slog; import android.view.SurfaceControlViewHost; import android.view.View; Loading @@ -41,6 +49,7 @@ import com.android.internal.util.function.pooled.PooledLambda; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; import java.util.concurrent.Executor; /** Loading Loading @@ -120,6 +129,7 @@ public abstract class GameSession { private LifecycleState mLifecycleState = LifecycleState.INITIALIZED; private boolean mAreTransientInsetsVisibleDueToGesture = false; private IGameSessionController mGameSessionController; private Context mContext; private int mTaskId; private GameSessionRootView mGameSessionRootView; private SurfaceControlViewHost mSurfaceControlViewHost; Loading @@ -137,6 +147,7 @@ public abstract class GameSession { int heightPx) { mGameSessionController = gameSessionController; mTaskId = taskId; mContext = context; mSurfaceControlViewHost = surfaceControlViewHost; mGameSessionRootView = new GameSessionRootView(context, mSurfaceControlViewHost); surfaceControlViewHost.setView(mGameSessionRootView, widthPx, heightPx); Loading Loading @@ -458,4 +469,67 @@ public abstract class GameSession { break; } } /** * Launches an activity within the same activity stack as the {@link GameSession}. When the * target activity exits, {@link GameSessionActivityCallback#onActivityResult(int, Intent)} will * be invoked with the result code and result data directly from the target activity (in other * words, the result code and data set via the target activity's * {@link android.app.Activity#startActivityForResult} call). The caller is expected to handle * the results that the target activity returns. * * <p>Any activity that an app would normally be able to start via {@link * android.app.Activity#startActivityForResult} will be startable via this method. * * <p>Started activities may see a different calling package than the game session's package * when calling {@link android.app.Activity#getCallingPackage()}. * * <p> If an exception is thrown while handling {@code intent}, * {@link GameSessionActivityCallback#onActivityStartFailed(Throwable)} will be called instead * of {@link GameSessionActivityCallback#onActivityResult(int, Intent)}. * * @param intent The intent to start. * @param options Additional options for how the Activity should be started. See * {@link android.app.Activity#startActivityForResult(Intent, int, Bundle)} for * more details. This value may be null. * @param executor Executor on which {@code callback} should be invoked. * @param callback Callback to be invoked once the started activity has finished. */ @RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY) public final void startActivityFromGameSessionForResult( @NonNull Intent intent, @Nullable Bundle options, @NonNull Executor executor, @NonNull GameSessionActivityCallback callback) { Objects.requireNonNull(intent); Objects.requireNonNull(executor); Objects.requireNonNull(callback); AndroidFuture<GameSessionActivityResult> future = new AndroidFuture<GameSessionActivityResult>() .whenCompleteAsync((result, ex) -> { if (ex != null) { callback.onActivityStartFailed(ex); return; } callback.onActivityResult(result.getResultCode(), result.getData()); }, executor); final Intent trampolineIntent = new Intent(); trampolineIntent.setComponent( new ComponentName( "android", "android.service.games.GameSessionTrampolineActivity")); trampolineIntent.putExtra(GameSessionTrampolineActivity.INTENT_KEY, intent); trampolineIntent.putExtra(GameSessionTrampolineActivity.OPTIONS_KEY, options); trampolineIntent.putExtra( GameSessionTrampolineActivity.FUTURE_KEY, future); try { int result = ActivityTaskManager.getService().startActivityFromGameSession( mContext.getIApplicationThread(), mContext.getPackageName(), "GameSession", Binder.getCallingPid(), Binder.getCallingUid(), trampolineIntent, mTaskId, UserHandle.myUserId()); Instrumentation.checkStartActivityResult(result, trampolineIntent); } catch (Throwable t) { executor.execute(() -> callback.onActivityStartFailed(t)); } } }
core/java/android/service/games/GameSessionActivityCallback.java 0 → 100644 +54 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 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.service.games; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.content.Intent; import android.os.Bundle; import java.util.concurrent.Executor; /** * Callback invoked when an activity launched via * {@link GameSession#startActivityFromGameSessionForResult(Intent, Bundle, Executor, * GameSessionActivityCallback)}} has returned a result or failed to start. * * @hide */ @SystemApi public interface GameSessionActivityCallback { /** * Callback invoked when an activity launched via * {@link GameSession#startActivityFromGameSessionForResult(Intent, Bundle, Executor, * GameSessionActivityCallback)}} has returned a result. * * @param resultCode The result code of the launched activity. See {@link * android.app.Activity#setResult(int)}. * @param data Any data returned by the launched activity. See {@link * android.app.Activity#setResult(int, Intent)}. */ void onActivityResult(int resultCode, @Nullable Intent data); /** * Callback invoked when a throwable was thrown when launching the {@link Intent} in * {@link GameSession#startActivityFromGameSessionForResult(Intent, Bundle, Executor, * GameSessionActivityCallback)}}. */ default void onActivityStartFailed(@NonNull Throwable t) {} }