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