1 /*
2  * Copyright (C) 2015 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 package com.android.car;
17 
18 import android.car.CarAppFocusManager;
19 import android.car.IAppFocus;
20 import android.car.IAppFocusListener;
21 import android.car.IAppFocusOwnershipCallback;
22 import android.content.Context;
23 import android.os.Binder;
24 import android.os.Handler;
25 import android.os.HandlerThread;
26 import android.os.Looper;
27 import android.os.Message;
28 import android.os.RemoteException;
29 import android.util.Log;
30 
31 import java.io.PrintWriter;
32 import java.util.HashMap;
33 import java.util.HashSet;
34 import java.util.Map;
35 import java.util.Set;
36 import java.util.concurrent.CopyOnWriteArrayList;
37 
38 /**
39  * App focus service ensures only one instance of application type is active at a time.
40  */
41 public class AppFocusService extends IAppFocus.Stub implements CarServiceBase,
42         BinderInterfaceContainer.BinderEventHandler<IAppFocusOwnershipCallback> {
43     private static final boolean DBG = false;
44     private static final boolean DBG_EVENT = false;
45 
46     private final SystemActivityMonitoringService mSystemActivityMonitoringService;
47     private final ClientHolder mAllChangeClients;
48     private final OwnershipClientHolder mAllOwnershipClients;
49     /** K: appType, V: client owning it */
50     private final HashMap<Integer, OwnershipClientInfo> mFocusOwners = new HashMap<>();
51     private final Set<Integer> mActiveAppTypes = new HashSet<>();
52     private final CopyOnWriteArrayList<FocusOwnershipCallback> mFocusOwnershipCallbacks =
53             new CopyOnWriteArrayList<>();
54     private final BinderInterfaceContainer.BinderEventHandler<IAppFocusListener>
55             mAllBinderEventHandler = bInterface -> { /* nothing to do.*/ };
56 
57     private DispatchHandler mDispatchHandler;
58     private HandlerThread mHandlerThread;
59 
AppFocusService(Context context, SystemActivityMonitoringService systemActivityMonitoringService)60     public AppFocusService(Context context,
61             SystemActivityMonitoringService systemActivityMonitoringService) {
62         mSystemActivityMonitoringService = systemActivityMonitoringService;
63         mAllChangeClients = new ClientHolder(mAllBinderEventHandler);
64         mAllOwnershipClients = new OwnershipClientHolder(this);
65     }
66 
67     @Override
registerFocusListener(IAppFocusListener listener, int appType)68     public void registerFocusListener(IAppFocusListener listener, int appType) {
69         synchronized (this) {
70             ClientInfo info = (ClientInfo) mAllChangeClients.getBinderInterface(listener);
71             if (info == null) {
72                 info = new ClientInfo(mAllChangeClients, listener, Binder.getCallingUid(),
73                         Binder.getCallingPid(), appType);
74                 mAllChangeClients.addBinderInterface(info);
75             } else {
76                 info.addAppType(appType);
77             }
78         }
79     }
80 
81     @Override
unregisterFocusListener(IAppFocusListener listener, int appType)82     public void unregisterFocusListener(IAppFocusListener listener, int appType) {
83         synchronized (this) {
84             ClientInfo info = (ClientInfo) mAllChangeClients.getBinderInterface(listener);
85             if (info == null) {
86                 return;
87             }
88             info.removeAppType(appType);
89             if (info.getAppTypes().isEmpty()) {
90                 mAllChangeClients.removeBinder(listener);
91             }
92         }
93     }
94 
95     @Override
getActiveAppTypes()96     public int[] getActiveAppTypes() {
97         synchronized (this) {
98             return toIntArray(mActiveAppTypes);
99         }
100     }
101 
102     @Override
isOwningFocus(IAppFocusOwnershipCallback callback, int appType)103     public boolean isOwningFocus(IAppFocusOwnershipCallback callback, int appType) {
104         synchronized (this) {
105             OwnershipClientInfo info =
106                     (OwnershipClientInfo) mAllOwnershipClients.getBinderInterface(callback);
107             if (info == null) {
108                 return false;
109             }
110             return info.getOwnedAppTypes().contains(appType);
111         }
112     }
113 
114     @Override
requestAppFocus(IAppFocusOwnershipCallback callback, int appType)115     public int requestAppFocus(IAppFocusOwnershipCallback callback, int appType) {
116         synchronized (this) {
117             OwnershipClientInfo info =
118                     (OwnershipClientInfo) mAllOwnershipClients.getBinderInterface(callback);
119             if (info == null) {
120                 info = new OwnershipClientInfo(mAllOwnershipClients, callback,
121                         Binder.getCallingUid(), Binder.getCallingPid());
122                 mAllOwnershipClients.addBinderInterface(info);
123             }
124             Set<Integer> alreadyOwnedAppTypes = info.getOwnedAppTypes();
125             if (!alreadyOwnedAppTypes.contains(appType)) {
126                 OwnershipClientInfo ownerInfo = mFocusOwners.get(appType);
127                 if (ownerInfo != null && ownerInfo != info) {
128                     if (mSystemActivityMonitoringService.isInForeground(
129                                 ownerInfo.getPid(), ownerInfo.getUid()) &&
130                         !mSystemActivityMonitoringService.isInForeground(
131                                 info.getPid(), info.getUid())) {
132                         Log.w(CarLog.TAG_APP_FOCUS, "Focus request failed for non-foreground app("
133                               + "pid=" + info.getPid() + ", uid=" + info.getUid() + ")."
134                               + "Foreground app (pid=" + ownerInfo.getPid() + ", uid="
135                               + ownerInfo.getUid() + ") owns it.");
136                         return CarAppFocusManager.APP_FOCUS_REQUEST_FAILED;
137                     }
138                     ownerInfo.removeOwnedAppType(appType);
139                     mDispatchHandler.requestAppFocusOwnershipLossDispatch(
140                             ownerInfo.binderInterface, appType);
141                     if (DBG) {
142                         Log.i(CarLog.TAG_APP_FOCUS, "losing app type "
143                                 + appType + "," + ownerInfo.toString());
144                     }
145                 }
146                 updateFocusOwner(appType, info);
147             }
148             info.addOwnedAppType(appType);
149             mDispatchHandler.requestAppFocusOwnershipGrantDispatch(
150                     info.binderInterface, appType);
151             if (mActiveAppTypes.add(appType)) {
152                 if (DBG) {
153                     Log.i(CarLog.TAG_APP_FOCUS, "adding active app type " + appType + ","
154                             + info.toString());
155                 }
156                 for (BinderInterfaceContainer.BinderInterface<IAppFocusListener> client :
157                         mAllChangeClients.getInterfaces()) {
158                     ClientInfo clientInfo = (ClientInfo) client;
159                     // dispatch events only when there is change after filter and the listener
160                     // is not coming from the current caller.
161                     if (clientInfo.getAppTypes().contains(appType)) {
162                         mDispatchHandler.requestAppFocusChangeDispatch(clientInfo.binderInterface,
163                                 appType, true);
164                     }
165                 }
166             }
167         }
168         return CarAppFocusManager.APP_FOCUS_REQUEST_SUCCEEDED;
169     }
170 
171     @Override
abandonAppFocus(IAppFocusOwnershipCallback callback, int appType)172     public void abandonAppFocus(IAppFocusOwnershipCallback callback, int appType) {
173         synchronized (this) {
174             OwnershipClientInfo info =
175                     (OwnershipClientInfo) mAllOwnershipClients.getBinderInterface(callback);
176             if (info == null) {
177                 // ignore as this client cannot have owned anything.
178                 return;
179             }
180             if (!mActiveAppTypes.contains(appType)) {
181                 // ignore as none of them are active;
182                 return;
183             }
184             Set<Integer> currentlyOwnedAppTypes = info.getOwnedAppTypes();
185             if (!currentlyOwnedAppTypes.contains(appType)) {
186                 // ignore as listener doesn't own focus.
187                 return;
188             }
189             if (mFocusOwners.remove(appType) != null) {
190                 mActiveAppTypes.remove(appType);
191                 info.removeOwnedAppType(appType);
192                 if (DBG) {
193                     Log.i(CarLog.TAG_APP_FOCUS, "abandoning focus " + appType
194                             + "," + info.toString());
195                 }
196                 for (FocusOwnershipCallback ownershipCallback : mFocusOwnershipCallbacks) {
197                     ownershipCallback.onFocusAbandoned(appType, info.mUid, info.mPid);
198                 }
199                 for (BinderInterfaceContainer.BinderInterface<IAppFocusListener> client :
200                         mAllChangeClients.getInterfaces()) {
201                     ClientInfo clientInfo = (ClientInfo) client;
202                     if (clientInfo.getAppTypes().contains(appType)) {
203                         mDispatchHandler.requestAppFocusChangeDispatch(clientInfo.binderInterface,
204                                 appType, false);
205                     }
206                 }
207             }
208         }
209     }
210 
211     @Override
init()212     public void init() {
213         synchronized (this) {
214             mHandlerThread = new HandlerThread(AppFocusService.class.getSimpleName());
215             mHandlerThread.start();
216             mDispatchHandler = new DispatchHandler(mHandlerThread.getLooper());
217         }
218     }
219 
220     @Override
release()221     public void release() {
222         synchronized (this) {
223             mHandlerThread.quitSafely();
224             try {
225                 mHandlerThread.join(1000);
226             } catch (InterruptedException e) {
227                 Log.e(CarLog.TAG_APP_FOCUS, "Timeout while waiting for handler thread to join.");
228             }
229             mDispatchHandler = null;
230             mAllChangeClients.clear();
231             mAllOwnershipClients.clear();
232             mFocusOwners.clear();
233             mActiveAppTypes.clear();
234         }
235     }
236 
237     @Override
onBinderDeath( BinderInterfaceContainer.BinderInterface<IAppFocusOwnershipCallback> bInterface)238     public void onBinderDeath(
239             BinderInterfaceContainer.BinderInterface<IAppFocusOwnershipCallback> bInterface) {
240         OwnershipClientInfo info = (OwnershipClientInfo) bInterface;
241         for (Integer appType : info.getOwnedAppTypes()) {
242             abandonAppFocus(bInterface.binderInterface, appType);
243         }
244     }
245 
246     @Override
dump(PrintWriter writer)247     public void dump(PrintWriter writer) {
248         writer.println("**AppFocusService**");
249         synchronized (this) {
250             writer.println("mActiveAppTypes:" + mActiveAppTypes);
251             for (BinderInterfaceContainer.BinderInterface<IAppFocusOwnershipCallback> client :
252                     mAllOwnershipClients.getInterfaces()) {
253                 OwnershipClientInfo clientInfo = (OwnershipClientInfo) client;
254                 writer.println(clientInfo.toString());
255             }
256         }
257     }
258 
259     /**
260      * Returns true if process with given uid and pid owns provided focus.
261      */
isFocusOwner(int uid, int pid, int appType)262     public boolean isFocusOwner(int uid, int pid, int appType) {
263         synchronized (this) {
264             if (mFocusOwners.containsKey(appType)) {
265                 OwnershipClientInfo clientInfo = mFocusOwners.get(appType);
266                 return clientInfo.getUid() == uid && clientInfo.getPid() == pid;
267             }
268         }
269         return false;
270     }
271 
272     /**
273      * Defines callback functions that will be called when ownership has been changed.
274      */
275     public interface FocusOwnershipCallback {
onFocusAcquired(int appType, int uid, int pid)276         void onFocusAcquired(int appType, int uid, int pid);
onFocusAbandoned(int appType, int uid, int pid)277         void onFocusAbandoned(int appType, int uid, int pid);
278     }
279 
280     /**
281      * Registers callback.
282      *
283      * If any focus already acquired it will trigger
284      * {@link FocusOwnershipCallback#onFocusAcquired} call immediately in the same thread.
285      */
registerContextOwnerChangedCallback(FocusOwnershipCallback callback)286     public void registerContextOwnerChangedCallback(FocusOwnershipCallback callback) {
287         mFocusOwnershipCallbacks.add(callback);
288 
289         HashSet<Map.Entry<Integer, OwnershipClientInfo>> owners;
290         synchronized (this) {
291             owners = new HashSet<>(mFocusOwners.entrySet());
292         }
293 
294         for (Map.Entry<Integer, OwnershipClientInfo> entry : owners) {
295             OwnershipClientInfo clientInfo = entry.getValue();
296             callback.onFocusAcquired(entry.getKey(), clientInfo.getUid(), clientInfo.getPid());
297         }
298     }
299 
300     /**
301      * Unregisters provided callback.
302      */
unregisterContextOwnerChangedCallback(FocusOwnershipCallback callback)303     public void unregisterContextOwnerChangedCallback(FocusOwnershipCallback callback) {
304         mFocusOwnershipCallbacks.remove(callback);
305     }
306 
updateFocusOwner(int appType, OwnershipClientInfo owner)307     private void updateFocusOwner(int appType, OwnershipClientInfo owner) {
308         CarServiceUtils.runOnMain(() -> {
309             synchronized (this) {
310                 mFocusOwners.put(appType, owner);
311             }
312 
313             for (FocusOwnershipCallback callback : mFocusOwnershipCallbacks) {
314                 callback.onFocusAcquired(appType, owner.getUid(), owner.getPid());
315             }
316         });
317     }
318 
dispatchAppFocusOwnershipLoss(IAppFocusOwnershipCallback callback, int appType)319     private void dispatchAppFocusOwnershipLoss(IAppFocusOwnershipCallback callback, int appType) {
320         try {
321             callback.onAppFocusOwnershipLost(appType);
322         } catch (RemoteException e) {
323         }
324     }
325 
dispatchAppFocusOwnershipGrant(IAppFocusOwnershipCallback callback, int appType)326     private void dispatchAppFocusOwnershipGrant(IAppFocusOwnershipCallback callback, int appType) {
327         try {
328             callback.onAppFocusOwnershipGranted(appType);
329         } catch (RemoteException e) {
330         }
331     }
332 
dispatchAppFocusChange(IAppFocusListener listener, int appType, boolean active)333     private void dispatchAppFocusChange(IAppFocusListener listener, int appType, boolean active) {
334         try {
335             listener.onAppFocusChanged(appType, active);
336         } catch (RemoteException e) {
337         }
338     }
339 
340     private static class ClientHolder extends BinderInterfaceContainer<IAppFocusListener> {
ClientHolder(BinderEventHandler<IAppFocusListener> holder)341         private ClientHolder(BinderEventHandler<IAppFocusListener> holder) {
342             super(holder);
343         }
344     }
345 
346     private static class OwnershipClientHolder extends
347             BinderInterfaceContainer<IAppFocusOwnershipCallback> {
OwnershipClientHolder(AppFocusService service)348         private OwnershipClientHolder(AppFocusService service) {
349             super(service);
350         }
351     }
352 
353     private static class ClientInfo extends
354             BinderInterfaceContainer.BinderInterface<IAppFocusListener> {
355         private final int mUid;
356         private final int mPid;
357         private final Set<Integer> mAppTypes = new HashSet<>();
358 
ClientInfo(ClientHolder holder, IAppFocusListener binder, int uid, int pid, int appType)359         private ClientInfo(ClientHolder holder, IAppFocusListener binder, int uid, int pid,
360                 int appType) {
361             super(holder, binder);
362             this.mUid = uid;
363             this.mPid = pid;
364             this.mAppTypes.add(appType);
365         }
366 
getAppTypes()367         private synchronized Set<Integer> getAppTypes() {
368             return mAppTypes;
369         }
370 
addAppType(Integer appType)371         private synchronized boolean addAppType(Integer appType) {
372             return mAppTypes.add(appType);
373         }
374 
removeAppType(Integer appType)375         private synchronized boolean removeAppType(Integer appType) {
376             return mAppTypes.remove(appType);
377         }
378 
379         @Override
toString()380         public String toString() {
381             synchronized (this) {
382                 return "ClientInfo{mUid=" + mUid + ",mPid=" + mPid
383                         + ",appTypes=" + mAppTypes + "}";
384             }
385         }
386     }
387 
388     private static class OwnershipClientInfo extends
389             BinderInterfaceContainer.BinderInterface<IAppFocusOwnershipCallback> {
390         private final int mUid;
391         private final int mPid;
392         private final Set<Integer> mOwnedAppTypes = new HashSet<>();
393 
OwnershipClientInfo(OwnershipClientHolder holder, IAppFocusOwnershipCallback binder, int uid, int pid)394         private OwnershipClientInfo(OwnershipClientHolder holder, IAppFocusOwnershipCallback binder,
395                 int uid, int pid) {
396             super(holder, binder);
397             this.mUid = uid;
398             this.mPid = pid;
399         }
400 
getOwnedAppTypes()401         private synchronized Set<Integer> getOwnedAppTypes() {
402             if (DBG_EVENT) {
403                 Log.i(CarLog.TAG_APP_FOCUS, "getOwnedAppTypes " + mOwnedAppTypes);
404             }
405             return mOwnedAppTypes;
406         }
407 
addOwnedAppType(Integer appType)408         private synchronized boolean addOwnedAppType(Integer appType) {
409             if (DBG_EVENT) {
410                 Log.i(CarLog.TAG_APP_FOCUS, "addOwnedAppType " + appType);
411             }
412             return mOwnedAppTypes.add(appType);
413         }
414 
removeOwnedAppType(Integer appType)415         private synchronized boolean removeOwnedAppType(Integer appType) {
416             if (DBG_EVENT) {
417                 Log.i(CarLog.TAG_APP_FOCUS, "removeOwnedAppType " + appType);
418             }
419             return mOwnedAppTypes.remove(appType);
420         }
421 
getUid()422         int getUid() {
423             return mUid;
424         }
425 
getPid()426         int getPid() {
427             return mPid;
428         }
429 
430         @Override
toString()431         public String toString() {
432             synchronized (this) {
433                 return "ClientInfo{mUid=" + mUid + ",mPid=" + mPid
434                         + ",owned=" + mOwnedAppTypes + "}";
435             }
436         }
437     }
438 
439     private class DispatchHandler extends Handler {
440         private static final int MSG_DISPATCH_OWNERSHIP_LOSS = 0;
441         private static final int MSG_DISPATCH_OWNERSHIP_GRANT = 1;
442         private static final int MSG_DISPATCH_FOCUS_CHANGE = 2;
443 
DispatchHandler(Looper looper)444         private DispatchHandler(Looper looper) {
445             super(looper);
446         }
447 
requestAppFocusOwnershipLossDispatch(IAppFocusOwnershipCallback callback, int appType)448         private void requestAppFocusOwnershipLossDispatch(IAppFocusOwnershipCallback callback,
449                 int appType) {
450             Message msg = obtainMessage(MSG_DISPATCH_OWNERSHIP_LOSS, appType, 0, callback);
451             sendMessage(msg);
452         }
453 
requestAppFocusOwnershipGrantDispatch(IAppFocusOwnershipCallback callback, int appType)454         private void requestAppFocusOwnershipGrantDispatch(IAppFocusOwnershipCallback callback,
455                 int appType) {
456             Message msg = obtainMessage(MSG_DISPATCH_OWNERSHIP_GRANT, appType, 0, callback);
457             sendMessage(msg);
458         }
459 
requestAppFocusChangeDispatch(IAppFocusListener listener, int appType, boolean active)460         private void requestAppFocusChangeDispatch(IAppFocusListener listener, int appType,
461                 boolean active) {
462             Message msg = obtainMessage(MSG_DISPATCH_FOCUS_CHANGE, appType, active ? 1 : 0,
463                     listener);
464             sendMessage(msg);
465         }
466 
467         @Override
handleMessage(Message msg)468         public void handleMessage(Message msg) {
469             switch (msg.what) {
470                 case MSG_DISPATCH_OWNERSHIP_LOSS:
471                     dispatchAppFocusOwnershipLoss((IAppFocusOwnershipCallback) msg.obj, msg.arg1);
472                     break;
473                 case MSG_DISPATCH_OWNERSHIP_GRANT:
474                     dispatchAppFocusOwnershipGrant((IAppFocusOwnershipCallback) msg.obj, msg.arg1);
475                     break;
476                 case MSG_DISPATCH_FOCUS_CHANGE:
477                     dispatchAppFocusChange((IAppFocusListener) msg.obj, msg.arg1, msg.arg2 == 1);
478                     break;
479                 default:
480                     Log.e(CarLog.TAG_APP_FOCUS, "Can't dispatch message: " + msg);
481             }
482         }
483     }
484 
toIntArray(Set<Integer> intSet)485     private static int[] toIntArray(Set<Integer> intSet) {
486         int[] intArr = new int[intSet.size()];
487         int index = 0;
488         for (Integer value : intSet) {
489             intArr[index++] = value;
490         }
491         return intArr;
492     }
493 }
494