1 /*
2  * Copyright (C) 2019 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 android.car.testapi;
18 
19 import android.bluetooth.BluetoothDevice;
20 import android.car.CarProjectionManager;
21 import android.car.CarProjectionManager.ProjectionAccessPointCallback;
22 import android.car.ICarProjection;
23 import android.car.ICarProjectionKeyEventHandler;
24 import android.car.ICarProjectionStatusListener;
25 import android.car.projection.ProjectionOptions;
26 import android.car.projection.ProjectionStatus;
27 import android.content.ComponentName;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.ServiceConnection;
31 import android.net.wifi.SoftApConfiguration;
32 import android.os.Bundle;
33 import android.os.IBinder;
34 import android.os.Message;
35 import android.os.Messenger;
36 import android.os.RemoteException;
37 
38 import java.util.ArrayList;
39 import java.util.BitSet;
40 import java.util.HashMap;
41 import java.util.List;
42 import java.util.Map;
43 
44 /**
45  * Fake implementation of {@link ICarProjection} interface.
46  *
47  * @hide
48  */
49 class FakeCarProjectionService extends ICarProjection.Stub implements
50         CarProjectionController {
51 
52     private final Context mContext;
53 
54     private SoftApConfiguration mSoftApConfiguration;
55     private Messenger mApMessenger;
56     private IBinder mApBinder;
57     private List<ICarProjectionStatusListener> mStatusListeners = new ArrayList<>();
58     private Map<IBinder, ProjectionStatus> mProjectionStatusMap = new HashMap<>();
59     private ProjectionStatus mCurrentProjectionStatus = ProjectionStatus.builder(
60             "", ProjectionStatus.PROJECTION_STATE_INACTIVE).build();
61     private ProjectionOptions mProjectionOptions;
62     private final Map<ICarProjectionKeyEventHandler, BitSet> mKeyEventListeners = new HashMap<>();
63 
64     private final ServiceConnection mServiceConnection = new ServiceConnection() {
65         @Override
66         public void onServiceConnected(ComponentName name, IBinder service) {}
67 
68         @Override
69         public void onServiceDisconnected(ComponentName name) {}
70     };
71 
FakeCarProjectionService(Context context)72     FakeCarProjectionService(Context context) {
73         mContext = context;
74         mProjectionOptions = ProjectionOptions.builder().build();
75     }
76 
77     @Override
registerProjectionRunner(Intent serviceIntent)78     public void registerProjectionRunner(Intent serviceIntent) throws RemoteException {
79         mContext.bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
80     }
81 
82     @Override
unregisterProjectionRunner(Intent serviceIntent)83     public void unregisterProjectionRunner(Intent serviceIntent) throws RemoteException {
84         mContext.unbindService(mServiceConnection);
85     }
86 
87     @Override
registerKeyEventHandler(ICarProjectionKeyEventHandler callback, byte[] events)88     public void registerKeyEventHandler(ICarProjectionKeyEventHandler callback, byte[] events) {
89         mKeyEventListeners.put(callback, BitSet.valueOf(events));
90     }
91 
92     @Override
unregisterKeyEventHandler(ICarProjectionKeyEventHandler callback)93     public void unregisterKeyEventHandler(ICarProjectionKeyEventHandler callback) {
94         mKeyEventListeners.remove(callback);
95     }
96 
97     @Override
fireKeyEvent(int event)98     public void fireKeyEvent(int event) {
99         for (Map.Entry<ICarProjectionKeyEventHandler, BitSet> entry :
100                 mKeyEventListeners.entrySet()) {
101             if (entry.getValue().get(event)) {
102                 try {
103                     entry.getKey().onKeyEvent(event);
104                 } catch (RemoteException e) {
105                     throw e.rethrowFromSystemServer();
106                 }
107             }
108         }
109     }
110 
111     @Override
startProjectionAccessPoint(Messenger messenger, IBinder binder)112     public void startProjectionAccessPoint(Messenger messenger, IBinder binder)
113             throws RemoteException {
114         mApMessenger = messenger;
115         mApBinder = binder;
116 
117         Message message = Message.obtain();
118         if (mSoftApConfiguration != null) {
119             message.what = CarProjectionManager.PROJECTION_AP_STARTED;
120             message.obj = mSoftApConfiguration;
121         } else {
122             message.what = CarProjectionManager.PROJECTION_AP_FAILED;
123             message.arg1 = ProjectionAccessPointCallback.ERROR_GENERIC;
124         }
125         messenger.send(message);
126     }
127 
128     @Override
stopProjectionAccessPoint(IBinder binder)129     public void stopProjectionAccessPoint(IBinder binder) throws RemoteException {
130         if (mApBinder == binder) {
131             Message message = Message.obtain();
132             message.what = CarProjectionManager.PROJECTION_AP_STOPPED;
133             mApMessenger.send(message);
134         }
135     }
136 
137     @Override
requestBluetoothProfileInhibit(BluetoothDevice device, int profile, IBinder token)138     public boolean requestBluetoothProfileInhibit(BluetoothDevice device, int profile,
139             IBinder token) throws RemoteException {
140         return true;
141     }
142 
143     @Override
releaseBluetoothProfileInhibit(BluetoothDevice device, int profile, IBinder token)144     public boolean releaseBluetoothProfileInhibit(BluetoothDevice device, int profile,
145             IBinder token) throws RemoteException {
146         return true;
147     }
148 
149     @Override
updateProjectionStatus(ProjectionStatus status, IBinder token)150     public void updateProjectionStatus(ProjectionStatus status, IBinder token)
151             throws RemoteException {
152         mCurrentProjectionStatus = status;
153         mProjectionStatusMap.put(token, status);
154         notifyStatusListeners(status,
155                 mStatusListeners.toArray(new ICarProjectionStatusListener[0]));
156     }
157 
notifyStatusListeners(ProjectionStatus status, ICarProjectionStatusListener... listeners)158     private void notifyStatusListeners(ProjectionStatus status,
159             ICarProjectionStatusListener... listeners) throws RemoteException {
160         for (ICarProjectionStatusListener listener : listeners) {
161             listener.onProjectionStatusChanged(
162                     status.getState(),
163                     status.getPackageName(),
164                     new ArrayList<>(mProjectionStatusMap.values()));
165         }
166     }
167 
168     @Override
registerProjectionStatusListener(ICarProjectionStatusListener listener)169     public void registerProjectionStatusListener(ICarProjectionStatusListener listener)
170             throws RemoteException {
171         mStatusListeners.add(listener);
172         notifyStatusListeners(mCurrentProjectionStatus, listener);
173     }
174 
175     @Override
unregisterProjectionStatusListener(ICarProjectionStatusListener listener)176     public void unregisterProjectionStatusListener(ICarProjectionStatusListener listener)
177             throws RemoteException {
178         mStatusListeners.remove(listener);
179     }
180 
181     @Override
setSoftApConfiguration(SoftApConfiguration softApConfiguration)182     public void setSoftApConfiguration(SoftApConfiguration softApConfiguration) {
183         mSoftApConfiguration = softApConfiguration;
184     }
185 
186     @Override
getProjectionOptions()187     public Bundle getProjectionOptions() throws RemoteException {
188         return mProjectionOptions.toBundle();
189     }
190 
191     @Override
getAvailableWifiChannels(int band)192     public int[] getAvailableWifiChannels(int band) throws RemoteException {
193         return new int[] {2412 /* Channel 1 */, 5180 /* Channel 36 */};
194     }
195 
196 
197     @Override
setProjectionOptions(ProjectionOptions projectionOptions)198     public void setProjectionOptions(ProjectionOptions projectionOptions) {
199         mProjectionOptions = projectionOptions;
200     }
201 }
202