1 /* 2 * Copyright (C) 2021 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 com.android.server.app; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.WorkerThread; 22 import android.content.BroadcastReceiver; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.os.PatternMatcher; 27 import android.text.TextUtils; 28 import android.util.Slog; 29 30 import com.android.internal.annotations.GuardedBy; 31 import com.android.server.SystemService; 32 import com.android.server.app.GameServiceConfiguration.GameServiceComponentConfiguration; 33 34 import java.util.Objects; 35 import java.util.concurrent.Executor; 36 37 /** 38 * Responsible for managing the Game Service API. 39 * 40 * Key responsibilities selecting the active Game Service provider, binding to the Game Service 41 * provider services, and driving the GameService/GameSession lifecycles. 42 */ 43 final class GameServiceController { 44 private static final String TAG = "GameServiceController"; 45 46 private final Object mLock = new Object(); 47 private final Context mContext; 48 private final Executor mBackgroundExecutor; 49 private final GameServiceProviderSelector mGameServiceProviderSelector; 50 private final GameServiceProviderInstanceFactory mGameServiceProviderInstanceFactory; 51 52 private volatile boolean mHasBootCompleted; 53 @Nullable 54 private volatile String mGameServiceProviderOverride; 55 @Nullable 56 private BroadcastReceiver mGameServicePackageChangedReceiver; 57 @Nullable 58 private volatile SystemService.TargetUser mCurrentForegroundUser; 59 @GuardedBy("mLock") 60 @Nullable 61 private volatile GameServiceComponentConfiguration mActiveGameServiceComponentConfiguration; 62 @GuardedBy("mLock") 63 @Nullable 64 private volatile GameServiceProviderInstance mGameServiceProviderInstance; 65 @GuardedBy("mLock") 66 @Nullable 67 private volatile String mActiveGameServiceProviderPackage; 68 GameServiceController( @onNull Context context, @NonNull Executor backgroundExecutor, @NonNull GameServiceProviderSelector gameServiceProviderSelector, @NonNull GameServiceProviderInstanceFactory gameServiceProviderInstanceFactory)69 GameServiceController( 70 @NonNull Context context, @NonNull Executor backgroundExecutor, 71 @NonNull GameServiceProviderSelector gameServiceProviderSelector, 72 @NonNull GameServiceProviderInstanceFactory gameServiceProviderInstanceFactory) { 73 mContext = context; 74 mGameServiceProviderInstanceFactory = gameServiceProviderInstanceFactory; 75 mBackgroundExecutor = backgroundExecutor; 76 mGameServiceProviderSelector = gameServiceProviderSelector; 77 } 78 onBootComplete()79 void onBootComplete() { 80 if (mHasBootCompleted) { 81 return; 82 } 83 mHasBootCompleted = true; 84 85 mBackgroundExecutor.execute(this::evaluateActiveGameServiceProvider); 86 } 87 notifyUserStarted(@onNull SystemService.TargetUser user)88 void notifyUserStarted(@NonNull SystemService.TargetUser user) { 89 if (mCurrentForegroundUser != null) { 90 return; 91 } 92 93 setCurrentForegroundUserAndEvaluateProvider(user); 94 } 95 notifyNewForegroundUser(@onNull SystemService.TargetUser user)96 void notifyNewForegroundUser(@NonNull SystemService.TargetUser user) { 97 setCurrentForegroundUserAndEvaluateProvider(user); 98 } 99 notifyUserUnlocking(@onNull SystemService.TargetUser user)100 void notifyUserUnlocking(@NonNull SystemService.TargetUser user) { 101 boolean isSameAsForegroundUser = 102 mCurrentForegroundUser != null 103 && mCurrentForegroundUser.getUserIdentifier() == user.getUserIdentifier(); 104 if (!isSameAsForegroundUser) { 105 return; 106 } 107 108 // It is likely that the Game Service provider's components are not Direct Boot mode aware 109 // and will not be capable of running until the user has unlocked the device. To allow for 110 // this we re-evaluate the active game service provider once these components are available. 111 112 mBackgroundExecutor.execute(this::evaluateActiveGameServiceProvider); 113 } 114 notifyUserStopped(@onNull SystemService.TargetUser user)115 void notifyUserStopped(@NonNull SystemService.TargetUser user) { 116 boolean isSameAsForegroundUser = 117 mCurrentForegroundUser != null 118 && mCurrentForegroundUser.getUserIdentifier() == user.getUserIdentifier(); 119 if (!isSameAsForegroundUser) { 120 return; 121 } 122 123 setCurrentForegroundUserAndEvaluateProvider(null); 124 } 125 setGameServiceProvider(@ullable String packageName)126 void setGameServiceProvider(@Nullable String packageName) { 127 boolean hasPackageChanged = !Objects.equals(mGameServiceProviderOverride, packageName); 128 if (!hasPackageChanged) { 129 return; 130 } 131 mGameServiceProviderOverride = packageName; 132 133 mBackgroundExecutor.execute(this::evaluateActiveGameServiceProvider); 134 } 135 setCurrentForegroundUserAndEvaluateProvider( @ullable SystemService.TargetUser user)136 private void setCurrentForegroundUserAndEvaluateProvider( 137 @Nullable SystemService.TargetUser user) { 138 boolean hasUserChanged = 139 !Objects.equals(mCurrentForegroundUser, user); 140 if (!hasUserChanged) { 141 return; 142 } 143 mCurrentForegroundUser = user; 144 145 mBackgroundExecutor.execute(this::evaluateActiveGameServiceProvider); 146 } 147 148 @WorkerThread evaluateActiveGameServiceProvider()149 private void evaluateActiveGameServiceProvider() { 150 if (!mHasBootCompleted) { 151 return; 152 } 153 154 synchronized (mLock) { 155 final GameServiceConfiguration selectedGameServiceConfiguration = 156 mGameServiceProviderSelector.get(mCurrentForegroundUser, 157 mGameServiceProviderOverride); 158 final String gameServicePackage = 159 selectedGameServiceConfiguration == null ? null : 160 selectedGameServiceConfiguration.getPackageName(); 161 final GameServiceComponentConfiguration gameServiceComponentConfiguration = 162 selectedGameServiceConfiguration == null ? null 163 : selectedGameServiceConfiguration 164 .getGameServiceComponentConfiguration(); 165 166 evaluateGameServiceProviderPackageChangedListenerLocked(gameServicePackage); 167 168 boolean didActiveGameServiceProviderChange = 169 !Objects.equals(gameServiceComponentConfiguration, 170 mActiveGameServiceComponentConfiguration); 171 if (!didActiveGameServiceProviderChange) { 172 return; 173 } 174 175 if (mGameServiceProviderInstance != null) { 176 Slog.i(TAG, "Stopping Game Service provider: " 177 + mActiveGameServiceComponentConfiguration); 178 mGameServiceProviderInstance.stop(); 179 mGameServiceProviderInstance = null; 180 } 181 182 mActiveGameServiceComponentConfiguration = gameServiceComponentConfiguration; 183 if (mActiveGameServiceComponentConfiguration == null) { 184 return; 185 } 186 187 Slog.i(TAG, 188 "Starting Game Service provider: " + mActiveGameServiceComponentConfiguration); 189 mGameServiceProviderInstance = 190 mGameServiceProviderInstanceFactory.create( 191 mActiveGameServiceComponentConfiguration); 192 mGameServiceProviderInstance.start(); 193 } 194 } 195 196 @GuardedBy("mLock") evaluateGameServiceProviderPackageChangedListenerLocked( @ullable String gameServicePackage)197 private void evaluateGameServiceProviderPackageChangedListenerLocked( 198 @Nullable String gameServicePackage) { 199 if (TextUtils.equals(mActiveGameServiceProviderPackage, gameServicePackage)) { 200 return; 201 } 202 203 if (mGameServicePackageChangedReceiver != null) { 204 mContext.unregisterReceiver(mGameServicePackageChangedReceiver); 205 mGameServicePackageChangedReceiver = null; 206 } 207 208 mActiveGameServiceProviderPackage = gameServicePackage; 209 210 if (TextUtils.isEmpty(mActiveGameServiceProviderPackage)) { 211 return; 212 } 213 214 IntentFilter intentFilter = new IntentFilter(); 215 intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 216 intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 217 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 218 intentFilter.addDataScheme("package"); 219 intentFilter.addDataSchemeSpecificPart(gameServicePackage, PatternMatcher.PATTERN_LITERAL); 220 mGameServicePackageChangedReceiver = new PackageChangedBroadcastReceiver( 221 gameServicePackage); 222 mContext.registerReceiver( 223 mGameServicePackageChangedReceiver, 224 intentFilter); 225 } 226 227 private final class PackageChangedBroadcastReceiver extends BroadcastReceiver { 228 private final String mPackageName; 229 PackageChangedBroadcastReceiver(String packageName)230 PackageChangedBroadcastReceiver(String packageName) { 231 mPackageName = packageName; 232 } 233 234 @Override onReceive(Context context, Intent intent)235 public void onReceive(Context context, Intent intent) { 236 if (!TextUtils.equals(intent.getData().getSchemeSpecificPart(), mPackageName)) { 237 return; 238 } 239 mBackgroundExecutor.execute( 240 GameServiceController.this::evaluateActiveGameServiceProvider); 241 } 242 } 243 } 244