Home | History | Annotate | Download | only in car
      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.CarAppContextManager;
     19 import android.car.IAppContext;
     20 import android.car.IAppContextListener;
     21 import android.content.Context;
     22 import android.os.Binder;
     23 import android.os.Handler;
     24 import android.os.HandlerThread;
     25 import android.os.Looper;
     26 import android.os.Message;
     27 import android.os.RemoteException;
     28 import android.util.Log;
     29 
     30 import com.android.car.hal.VehicleHal;
     31 
     32 import java.io.PrintWriter;
     33 import java.util.HashMap;
     34 
     35 public class AppContextService extends IAppContext.Stub implements CarServiceBase,
     36         BinderInterfaceContainer.BinderEventHandler<IAppContextListener> {
     37     private static final boolean DBG = true;
     38     private static final boolean DBG_EVENT = false;
     39 
     40     private final ClientHolder mAllClients;
     41     /** K: context flag, V: client owning it */
     42     private final HashMap<Integer, ClientInfo> mContextOwners = new HashMap<>();
     43     private int mActiveContexts;
     44 
     45     private final HandlerThread mHandlerThread;
     46     private final DispatchHandler mDispatchHandler;
     47 
     48     public AppContextService(Context context) {
     49         mAllClients = new ClientHolder(this);
     50         mHandlerThread = new HandlerThread(AppContextService.class.getSimpleName());
     51         mHandlerThread.start();
     52         mDispatchHandler = new DispatchHandler(mHandlerThread.getLooper());
     53     }
     54 
     55     @Override
     56     public void registerContextListener(IAppContextListener listener, int filter) {
     57         synchronized (this) {
     58             ClientInfo info = (ClientInfo) mAllClients.getBinderInterface(listener);
     59             if (info == null) {
     60                 info = new ClientInfo(mAllClients, listener, Binder.getCallingUid(),
     61                         Binder.getCallingPid(), filter);
     62                 mAllClients.addBinderInterface(info);
     63             } else {
     64                 info.setFilter(filter);
     65             }
     66         }
     67     }
     68 
     69     @Override
     70     public void unregisterContextListener(IAppContextListener listener) {
     71         synchronized (this) {
     72             ClientInfo info = (ClientInfo) mAllClients.getBinderInterface(listener);
     73             if (info == null) {
     74                 return;
     75             }
     76             resetActiveContexts(listener, info.getOwnedContexts());
     77             mAllClients.removeBinder(listener);
     78         }
     79     }
     80 
     81     @Override
     82     public int getActiveAppContexts() {
     83         synchronized (this) {
     84             return mActiveContexts;
     85         }
     86     }
     87 
     88     @Override
     89     public boolean isOwningContext(IAppContextListener listener, int context) {
     90         synchronized (this) {
     91             ClientInfo info = (ClientInfo) mAllClients.getBinderInterface(listener);
     92             if (info == null) {
     93                 return false;
     94             }
     95             int ownedContexts = info.getOwnedContexts();
     96             return (ownedContexts & context) == context;
     97         }
     98     }
     99 
    100     @Override
    101     public void setActiveContexts(IAppContextListener listener, int contexts) {
    102         synchronized (this) {
    103             ClientInfo info = (ClientInfo) mAllClients.getBinderInterface(listener);
    104             if (info == null) {
    105                 throw new IllegalStateException("listener not registered");
    106             }
    107             int alreadyOwnedContexts = info.getOwnedContexts();
    108             int addedContexts = 0;
    109             for (int c = CarAppContextManager.APP_CONTEXT_START_FLAG;
    110                     c <= CarAppContextManager.APP_CONTEXT_END_FLAG; c = (c << 1)) {
    111                 if ((c & contexts) != 0 && (c & alreadyOwnedContexts) == 0) {
    112                     ClientInfo ownerInfo = mContextOwners.get(c);
    113                     if (ownerInfo != null && ownerInfo != info) {
    114                         //TODO check if current owner is having fore-ground activity. If yes,
    115                         //reject request. Always grant if requestor is fore-ground activity.
    116                         ownerInfo.setOwnedContexts(ownerInfo.getOwnedContexts() & ~c);
    117                         mDispatchHandler.requestAppContextOwnershipLossDispatch(
    118                                 ownerInfo.binderInterface, c);
    119                         if (DBG) {
    120                             Log.i(CarLog.TAG_APP_CONTEXT, "losing context " +
    121                                     Integer.toHexString(c) + "," + ownerInfo.toString());
    122                         }
    123                     } else {
    124                         addedContexts |= c;
    125                     }
    126                     mContextOwners.put(c, info);
    127                 }
    128             }
    129             info.setOwnedContexts(alreadyOwnedContexts | contexts);
    130             mActiveContexts |= addedContexts;
    131             if (addedContexts != 0) {
    132                 if (DBG) {
    133                     Log.i(CarLog.TAG_APP_CONTEXT, "setting context " +
    134                             Integer.toHexString(addedContexts) + "," + info.toString());
    135                 }
    136                 for (BinderInterfaceContainer.BinderInterface<IAppContextListener> client :
    137                     mAllClients.getInterfaces()) {
    138                     ClientInfo clientInfo = (ClientInfo) client;
    139                     // dispatch events only when there is change after filter and the listener
    140                     // is not coming from the current caller.
    141                     int clientFilter = clientInfo.getFilter();
    142                     if ((addedContexts & clientFilter) != 0 && clientInfo != info) {
    143                         mDispatchHandler.requestAppContextChangeDispatch(clientInfo.binderInterface,
    144                                 mActiveContexts & clientFilter);
    145                     }
    146                 }
    147             }
    148         }
    149     }
    150 
    151     @Override
    152     public void resetActiveContexts(IAppContextListener listener, int contexts) {
    153         synchronized (this) {
    154             ClientInfo info = (ClientInfo) mAllClients.getBinderInterface(listener);
    155             if (info == null) {
    156                 // ignore as this client cannot have owned anything.
    157                 return;
    158             }
    159             if ((contexts & mActiveContexts) == 0) {
    160                 // ignore as none of them are active;
    161                 return;
    162             }
    163             int removedContexts = 0;
    164             int currentlyOwnedContexts = info.getOwnedContexts();
    165             for (int c = CarAppContextManager.APP_CONTEXT_START_FLAG;
    166                     c <= CarAppContextManager.APP_CONTEXT_END_FLAG; c = (c << 1)) {
    167                 if ((c & contexts) != 0 && (c & currentlyOwnedContexts) != 0) {
    168                     removedContexts |= c;
    169                     mContextOwners.remove(c);
    170                 }
    171             }
    172             if (removedContexts != 0) {
    173                 mActiveContexts &= ~removedContexts;
    174                 info.setOwnedContexts(currentlyOwnedContexts & ~removedContexts);
    175                 if (DBG) {
    176                     Log.i(CarLog.TAG_APP_CONTEXT, "resetting context " +
    177                             Integer.toHexString(removedContexts) + "," + info.toString());
    178                 }
    179                 for (BinderInterfaceContainer.BinderInterface<IAppContextListener> client :
    180                     mAllClients.getInterfaces()) {
    181                     ClientInfo clientInfo = (ClientInfo) client;
    182                     int clientFilter = clientInfo.getFilter();
    183                     if ((removedContexts & clientFilter) != 0 && clientInfo != info) {
    184                         mDispatchHandler.requestAppContextChangeDispatch(clientInfo.binderInterface,
    185                                 mActiveContexts & clientFilter);
    186                     }
    187                 }
    188             }
    189         }
    190     }
    191 
    192     @Override
    193     public void init() {
    194         // nothing to do
    195     }
    196 
    197     @Override
    198     public void release() {
    199         synchronized (this) {
    200             mAllClients.clear();
    201             mContextOwners.clear();
    202             mActiveContexts = 0;
    203         }
    204     }
    205 
    206     @Override
    207     public void onBinderDeath(
    208             BinderInterfaceContainer.BinderInterface<IAppContextListener> bInterface) {
    209         ClientInfo info = (ClientInfo) bInterface;
    210         int ownedContexts = info.getOwnedContexts();
    211         if (ownedContexts != 0) {
    212             resetActiveContexts(bInterface.binderInterface, ownedContexts);
    213         }
    214     }
    215 
    216     @Override
    217     public void dump(PrintWriter writer) {
    218         writer.println("**AppContextService**");
    219         synchronized (this) {
    220             writer.println("mActiveContexts:" + Integer.toHexString(mActiveContexts));
    221             for (BinderInterfaceContainer.BinderInterface<IAppContextListener> client :
    222                 mAllClients.getInterfaces()) {
    223                 ClientInfo clientInfo = (ClientInfo) client;
    224                 writer.println(clientInfo.toString());
    225             }
    226         }
    227     }
    228 
    229     /**
    230      * Returns true if process with given uid and pid owns provided context.
    231      */
    232     public boolean isContextOwner(int uid, int pid, int context) {
    233         synchronized (this) {
    234             if (mContextOwners.containsKey(context)) {
    235                 ClientInfo clientInfo = mContextOwners.get(context);
    236                 return clientInfo.uid == uid && clientInfo.pid == pid;
    237             }
    238         }
    239         return false;
    240     }
    241 
    242     private void dispatchAppContextOwnershipLoss(IAppContextListener listener, int contexts) {
    243         try {
    244             listener.onAppContextOwnershipLoss(contexts);
    245         } catch (RemoteException e) {
    246         }
    247     }
    248 
    249     private void dispatchAppContextChange(IAppContextListener listener, int contexts) {
    250         try {
    251             listener.onAppContextChange(contexts);
    252         } catch (RemoteException e) {
    253         }
    254     }
    255 
    256     private static class ClientHolder extends BinderInterfaceContainer<IAppContextListener> {
    257         private ClientHolder(AppContextService service) {
    258             super(service);
    259         }
    260     }
    261 
    262     private static class ClientInfo extends
    263             BinderInterfaceContainer.BinderInterface<IAppContextListener> {
    264         private final int uid;
    265         private final int pid;
    266         private int mFilter;
    267         /** contexts owned by this client */
    268         private int mOwnedContexts;
    269 
    270         private ClientInfo(ClientHolder holder, IAppContextListener binder, int uid, int pid,
    271                 int filter) {
    272             super(holder, binder);
    273             this.uid = uid;
    274             this.pid = pid;
    275             this.mFilter = filter;
    276         }
    277 
    278         private synchronized int getFilter() {
    279             return mFilter;
    280         }
    281 
    282         private synchronized void setFilter(int filter) {
    283             mFilter = filter;
    284         }
    285 
    286         private synchronized int getOwnedContexts() {
    287             if (DBG_EVENT) {
    288                 Log.i(CarLog.TAG_APP_CONTEXT, "getOwnedContexts " +
    289                         Integer.toHexString(mOwnedContexts));
    290             }
    291             return mOwnedContexts;
    292         }
    293 
    294         private synchronized void setOwnedContexts(int contexts) {
    295             if (DBG_EVENT) {
    296                 Log.i(CarLog.TAG_APP_CONTEXT, "setOwnedContexts " + Integer.toHexString(contexts));
    297             }
    298             mOwnedContexts = contexts;
    299         }
    300 
    301         @Override
    302         public String toString() {
    303             synchronized (this) {
    304                 return "ClientInfo{uid=" + uid + ",pid=" + pid +
    305                         ",filter=" + Integer.toHexString(mFilter) +
    306                         ",owned=" + Integer.toHexString(mOwnedContexts) + "}";
    307             }
    308         }
    309     }
    310 
    311     private class DispatchHandler extends Handler {
    312         private static final int MSG_DISPATCH_OWNERSHIP_LOSS = 0;
    313         private static final int MSG_DISPATCH_CONTEXT_CHANGE = 1;
    314 
    315         private DispatchHandler(Looper looper) {
    316             super(looper);
    317         }
    318 
    319         private void requestAppContextOwnershipLossDispatch(IAppContextListener listener,
    320                 int contexts) {
    321             Message msg = obtainMessage(MSG_DISPATCH_OWNERSHIP_LOSS, contexts, 0, listener);
    322             sendMessage(msg);
    323         }
    324 
    325         private void requestAppContextChangeDispatch(IAppContextListener listener, int contexts) {
    326             Message msg = obtainMessage(MSG_DISPATCH_CONTEXT_CHANGE, contexts, 0, listener);
    327             sendMessage(msg);
    328         }
    329 
    330         @Override
    331         public void handleMessage(Message msg) {
    332             switch (msg.what) {
    333                 case MSG_DISPATCH_OWNERSHIP_LOSS:
    334                     dispatchAppContextOwnershipLoss((IAppContextListener) msg.obj, msg.arg1);
    335                     break;
    336                 case MSG_DISPATCH_CONTEXT_CHANGE:
    337                     dispatchAppContextChange((IAppContextListener) msg.obj, msg.arg1);
    338                     break;
    339             }
    340         }
    341     }
    342 }
    343