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