1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.service.games; 18 19 import android.app.Service; 20 import android.content.ComponentName; 21 import android.content.Intent; 22 import android.content.pm.PackageManager; 23 import android.graphics.Rect; 24 import android.os.Bundle; 25 import android.os.Handler; 26 import android.os.IBinder; 27 import android.os.Looper; 28 import android.service.games.GameSession.ScreenshotCallback; 29 import android.service.games.TestGameSessionService.TestGameSession; 30 import android.service.games.testing.ActivityResult; 31 import android.service.games.testing.GameSessionEventInfo; 32 import android.service.games.testing.IGameServiceTestService; 33 import android.service.games.testing.OnSystemBarVisibilityChangedInfo; 34 35 import androidx.annotation.NonNull; 36 import androidx.annotation.Nullable; 37 38 import com.android.compatibility.common.util.PollingCheck; 39 40 import com.google.common.collect.ImmutableList; 41 42 import java.util.List; 43 import java.util.concurrent.CountDownLatch; 44 import java.util.concurrent.TimeUnit; 45 46 /** 47 * Service allowing external apps to verify the state of {@link TestGameService} and {@link 48 * TestGameSessionService}. 49 */ 50 public final class GameServiceTestService extends Service { 51 52 private static final long SCREENSHOT_CALLBACK_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(15); 53 54 @Nullable 55 private ActivityResult mLastActivityResult; 56 private final IGameServiceTestService.Stub mStub = new IGameServiceTestService.Stub() { 57 private final Handler mHandler = new Handler(Looper.getMainLooper()); 58 59 @Override 60 public boolean isGameServiceConnected() { 61 return TestGameService.isConnected(); 62 } 63 64 @Override 65 public void setGamePackageNames(List<String> gamePackageNames) { 66 TestGameService.setGamePackages(gamePackageNames); 67 } 68 69 @Override 70 public List<String> getActiveSessions() { 71 return ImmutableList.copyOf(TestGameSessionService.getActiveSessions()); 72 } 73 74 @Override 75 public List<GameSessionEventInfo> getGameSessionEventHistory() { 76 return ImmutableList.copyOf(TestGameSessionService.getGameSessionEventHistory()); 77 } 78 79 @Override 80 public void resetState() { 81 TestGameService.reset(); 82 TestGameSessionService.reset(); 83 mLastActivityResult = null; 84 85 setGameServiceComponentEnabled(true); 86 setGameSessionServiceComponentEnabled(true); 87 } 88 89 @Override 90 public int getFocusedTaskId() { 91 TestGameSession focusedGameSession = TestGameSessionService.getFocusedSession(); 92 if (focusedGameSession == null) { 93 return -1; 94 } 95 96 return focusedGameSession.getTaskId(); 97 } 98 99 @Override 100 public void startGameSessionActivity(Intent intent, Bundle options) { 101 TestGameSession focusedGameSession = TestGameSessionService.getFocusedSession(); 102 if (focusedGameSession == null) { 103 return; 104 } 105 106 focusedGameSession.startActivityFromGameSessionForResult(intent, options, 107 mHandler::post, new GameSessionActivityCallback() { 108 @Override 109 public void onActivityResult(int resultCode, 110 @Nullable Intent data) { 111 mLastActivityResult = ActivityResult.forSuccess( 112 focusedGameSession.getPackageName(), 113 resultCode, 114 data); 115 } 116 117 @Override 118 public void onActivityStartFailed(@NonNull Throwable t) { 119 mLastActivityResult = ActivityResult.forError( 120 focusedGameSession.getPackageName(), t); 121 } 122 }); 123 } 124 125 @Override 126 public ActivityResult getLastActivityResult() { 127 if (mLastActivityResult == null) { 128 PollingCheck.waitFor(() -> mLastActivityResult != null); 129 } 130 131 return mLastActivityResult; 132 } 133 134 @Override 135 public Rect getTouchableOverlayBounds() { 136 TestGameSession focusedGameSession = TestGameSessionService.getFocusedSession(); 137 if (focusedGameSession == null) { 138 return null; 139 } 140 141 return focusedGameSession.getTouchableBounds(); 142 } 143 144 @Override 145 public void restartFocusedGameSession() { 146 TestGameSession focusedGameSession = TestGameSessionService.getFocusedSession(); 147 if (focusedGameSession == null) { 148 return; 149 } 150 focusedGameSession.restartGame(); 151 } 152 153 @Override 154 public boolean takeScreenshotForFocusedGameSession() { 155 boolean result = false; 156 TestGameSession focusedGameSession = TestGameSessionService.getFocusedSession(); 157 if (focusedGameSession != null) { 158 CountDownLatch countDownLatch = new CountDownLatch(1); 159 final boolean[] ret = new boolean[1]; 160 ScreenshotCallback callback = 161 new ScreenshotCallback() { 162 @Override 163 public void onFailure(int statusCode) { 164 ret[0] = false; 165 countDownLatch.countDown(); 166 } 167 168 @Override 169 public void onSuccess() { 170 ret[0] = true; 171 countDownLatch.countDown(); 172 } 173 }; 174 focusedGameSession.takeScreenshot(Runnable::run, callback); 175 try { 176 countDownLatch.await( 177 SCREENSHOT_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS); 178 } catch (InterruptedException e) { 179 return false; 180 } 181 result = ret[0]; 182 } 183 return result; 184 } 185 186 public OnSystemBarVisibilityChangedInfo getOnSystemBarVisibilityChangedInfo() { 187 TestGameSession focusedGameSession = TestGameSessionService.getFocusedSession(); 188 if (focusedGameSession == null) { 189 return null; 190 } 191 return focusedGameSession.getOnSystemBarVisibilityChangedInfo(); 192 } 193 194 public void setGameServiceComponentEnabled(boolean enabled) { 195 getPackageManager().setComponentEnabledSetting( 196 new ComponentName(getApplicationContext(), TestGameService.class), 197 enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED 198 : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 199 PackageManager.DONT_KILL_APP | PackageManager.SYNCHRONOUS); 200 201 if (enabled) { 202 return; 203 } 204 205 // Wait for package changes to propagate and then reset the TestGameService connection 206 // state. 207 try { 208 Thread.sleep(3_000L); 209 } catch (InterruptedException e) { 210 // Do nothing. 211 } 212 TestGameService.reset(); 213 } 214 215 public void setGameSessionServiceComponentEnabled(boolean enabled) { 216 getPackageManager().setComponentEnabledSetting( 217 new ComponentName(getApplicationContext(), TestGameSessionService.class), 218 enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED 219 : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 220 PackageManager.DONT_KILL_APP | PackageManager.SYNCHRONOUS); 221 } 222 }; 223 224 @Nullable 225 @Override onBind(Intent intent)226 public IBinder onBind(Intent intent) { 227 return mStub.asBinder(); 228 } 229 } 230