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