1/*
2 * Copyright 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 */
16package com.android.server.camera;
17
18import android.content.BroadcastReceiver;
19import android.content.Context;
20import android.content.Intent;
21import android.content.IntentFilter;
22import android.hardware.ICameraService;
23import android.hardware.ICameraServiceProxy;
24import android.nfc.INfcAdapter;
25import android.os.Binder;
26import android.os.Handler;
27import android.os.IBinder;
28import android.os.Message;
29import android.os.Process;
30import android.os.RemoteException;
31import android.os.SystemProperties;
32import android.os.UserManager;
33import android.util.ArraySet;
34import android.util.Slog;
35
36import com.android.server.ServiceThread;
37import com.android.server.SystemService;
38
39import java.util.Collection;
40import java.util.Set;
41
42/**
43 * CameraServiceProxy is the system_server analog to the camera service running in mediaserver.
44 *
45 * @hide
46 */
47public class CameraServiceProxy extends SystemService
48        implements Handler.Callback, IBinder.DeathRecipient {
49    private static final String TAG = "CameraService_proxy";
50    private static final boolean DEBUG = false;
51
52    /**
53     * This must match the ICameraService.aidl definition
54     */
55    private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
56
57    public static final String CAMERA_SERVICE_PROXY_BINDER_NAME = "media.camera.proxy";
58
59    // State arguments to use with the notifyCameraState call from camera service:
60    public static final int CAMERA_STATE_OPEN = 0;
61    public static final int CAMERA_STATE_ACTIVE = 1;
62    public static final int CAMERA_STATE_IDLE = 2;
63    public static final int CAMERA_STATE_CLOSED = 3;
64
65    // Flags arguments to NFC adapter to enable/disable NFC
66    public static final int DISABLE_POLLING_FLAGS = 0x1000;
67    public static final int ENABLE_POLLING_FLAGS = 0x0000;
68
69    // Handler message codes
70    private static final int MSG_SWITCH_USER = 1;
71
72    private static final int RETRY_DELAY_TIME = 20; //ms
73
74    private final Context mContext;
75    private final ServiceThread mHandlerThread;
76    private final Handler mHandler;
77    private UserManager mUserManager;
78
79    private final Object mLock = new Object();
80    private Set<Integer> mEnabledCameraUsers;
81    private int mLastUser;
82
83    private ICameraService mCameraServiceRaw;
84
85    private final ArraySet<String> mActiveCameraIds = new ArraySet<>();
86
87    private static final String NFC_NOTIFICATION_PROP = "ro.camera.notify_nfc";
88    private static final String NFC_SERVICE_BINDER_NAME = "nfc";
89    private static final IBinder nfcInterfaceToken = new Binder();
90
91    private final boolean mNotifyNfc;
92    private int mActiveCameraCount = 0;
93
94    private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
95        @Override
96        public void onReceive(Context context, Intent intent) {
97            final String action = intent.getAction();
98            if (action == null) return;
99
100            switch (action) {
101                case Intent.ACTION_USER_ADDED:
102                case Intent.ACTION_USER_REMOVED:
103                case Intent.ACTION_USER_INFO_CHANGED:
104                case Intent.ACTION_MANAGED_PROFILE_ADDED:
105                case Intent.ACTION_MANAGED_PROFILE_REMOVED:
106                    synchronized(mLock) {
107                        // Return immediately if we haven't seen any users start yet
108                        if (mEnabledCameraUsers == null) return;
109                        switchUserLocked(mLastUser);
110                    }
111                    break;
112                default:
113                    break; // do nothing
114            }
115
116        }
117    };
118
119    private final ICameraServiceProxy.Stub mCameraServiceProxy = new ICameraServiceProxy.Stub() {
120        @Override
121        public void pingForUserUpdate() {
122            notifySwitchWithRetries(30);
123        }
124
125        @Override
126        public void notifyCameraState(String cameraId, int newCameraState) {
127            String state = cameraStateToString(newCameraState);
128            if (DEBUG) Slog.v(TAG, "Camera " + cameraId + " state now " + state);
129
130            updateActivityCount(cameraId, newCameraState);
131        }
132    };
133
134    public CameraServiceProxy(Context context) {
135        super(context);
136        mContext = context;
137        mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_DISPLAY, /*allowTo*/false);
138        mHandlerThread.start();
139        mHandler = new Handler(mHandlerThread.getLooper(), this);
140
141        mNotifyNfc = SystemProperties.getInt(NFC_NOTIFICATION_PROP, 0) > 0;
142        if (DEBUG) Slog.v(TAG, "Notify NFC behavior is " + (mNotifyNfc ? "active" : "disabled"));
143    }
144
145    @Override
146    public boolean handleMessage(Message msg) {
147        switch(msg.what) {
148            case MSG_SWITCH_USER: {
149                notifySwitchWithRetries(msg.arg1);
150            } break;
151            default: {
152                Slog.e(TAG, "CameraServiceProxy error, invalid message: " + msg.what);
153            } break;
154        }
155        return true;
156    }
157
158    @Override
159    public void onStart() {
160        mUserManager = UserManager.get(mContext);
161        if (mUserManager == null) {
162            // Should never see this unless someone messes up the SystemServer service boot order.
163            throw new IllegalStateException("UserManagerService must start before" +
164                    " CameraServiceProxy!");
165        }
166
167        IntentFilter filter = new IntentFilter();
168        filter.addAction(Intent.ACTION_USER_ADDED);
169        filter.addAction(Intent.ACTION_USER_REMOVED);
170        filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
171        filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
172        filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
173        mContext.registerReceiver(mIntentReceiver, filter);
174
175        publishBinderService(CAMERA_SERVICE_PROXY_BINDER_NAME, mCameraServiceProxy);
176    }
177
178    @Override
179    public void onStartUser(int userHandle) {
180        synchronized(mLock) {
181            if (mEnabledCameraUsers == null) {
182                // Initialize mediaserver, or update mediaserver if we are recovering from a crash.
183                switchUserLocked(userHandle);
184            }
185        }
186    }
187
188    @Override
189    public void onSwitchUser(int userHandle) {
190        synchronized(mLock) {
191            switchUserLocked(userHandle);
192        }
193    }
194
195    /**
196     * Handle the death of the native camera service
197     */
198    @Override
199    public void binderDied() {
200        if (DEBUG) Slog.w(TAG, "Native camera service has died");
201        synchronized(mLock) {
202            mCameraServiceRaw = null;
203
204            // All cameras reset to idle on camera service death
205            boolean wasEmpty = mActiveCameraIds.isEmpty();
206            mActiveCameraIds.clear();
207
208            if ( mNotifyNfc && !wasEmpty ) {
209                notifyNfcService(/*enablePolling*/ true);
210            }
211        }
212    }
213
214    private void switchUserLocked(int userHandle) {
215        Set<Integer> currentUserHandles = getEnabledUserHandles(userHandle);
216        mLastUser = userHandle;
217        if (mEnabledCameraUsers == null || !mEnabledCameraUsers.equals(currentUserHandles)) {
218            // Some user handles have been added or removed, update mediaserver.
219            mEnabledCameraUsers = currentUserHandles;
220            notifyMediaserverLocked(ICameraService.EVENT_USER_SWITCHED, currentUserHandles);
221        }
222    }
223
224    private Set<Integer> getEnabledUserHandles(int currentUserHandle) {
225        int[] userProfiles = mUserManager.getEnabledProfileIds(currentUserHandle);
226        Set<Integer> handles = new ArraySet<>(userProfiles.length);
227
228        for (int id : userProfiles) {
229            handles.add(id);
230        }
231
232        return handles;
233    }
234
235    private void notifySwitchWithRetries(int retries) {
236        synchronized(mLock) {
237            if (mEnabledCameraUsers == null) {
238                return;
239            }
240            if (notifyMediaserverLocked(ICameraService.EVENT_USER_SWITCHED, mEnabledCameraUsers)) {
241                retries = 0;
242            }
243        }
244        if (retries <= 0) {
245            return;
246        }
247        Slog.i(TAG, "Could not notify camera service of user switch, retrying...");
248        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SWITCH_USER, retries - 1, 0, null),
249                RETRY_DELAY_TIME);
250    }
251
252    private boolean notifyMediaserverLocked(int eventType, Set<Integer> updatedUserHandles) {
253        // Forward the user switch event to the native camera service running in the mediaserver
254        // process.
255        if (mCameraServiceRaw == null) {
256            IBinder cameraServiceBinder = getBinderService(CAMERA_SERVICE_BINDER_NAME);
257            if (cameraServiceBinder == null) {
258                Slog.w(TAG, "Could not notify mediaserver, camera service not available.");
259                return false; // Camera service not active, cannot evict user clients.
260            }
261            try {
262                cameraServiceBinder.linkToDeath(this, /*flags*/ 0);
263            } catch (RemoteException e) {
264                Slog.w(TAG, "Could not link to death of native camera service");
265                return false;
266            }
267
268            mCameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
269        }
270
271        try {
272            mCameraServiceRaw.notifySystemEvent(eventType, toArray(updatedUserHandles));
273        } catch (RemoteException e) {
274            Slog.w(TAG, "Could not notify mediaserver, remote exception: " + e);
275            // Not much we can do if camera service is dead.
276            return false;
277        }
278        return true;
279    }
280
281    private void updateActivityCount(String cameraId, int newCameraState) {
282        synchronized(mLock) {
283            boolean wasEmpty = mActiveCameraIds.isEmpty();
284            switch (newCameraState) {
285                case CAMERA_STATE_OPEN:
286                    break;
287                case CAMERA_STATE_ACTIVE:
288                    mActiveCameraIds.add(cameraId);
289                    break;
290                case CAMERA_STATE_IDLE:
291                case CAMERA_STATE_CLOSED:
292                    mActiveCameraIds.remove(cameraId);
293                    break;
294            }
295            boolean isEmpty = mActiveCameraIds.isEmpty();
296            if ( mNotifyNfc && (wasEmpty != isEmpty) ) {
297                notifyNfcService(isEmpty);
298            }
299        }
300    }
301
302    private void notifyNfcService(boolean enablePolling) {
303
304        IBinder nfcServiceBinder = getBinderService(NFC_SERVICE_BINDER_NAME);
305        if (nfcServiceBinder == null) {
306            Slog.w(TAG, "Could not connect to NFC service to notify it of camera state");
307            return;
308        }
309        INfcAdapter nfcAdapterRaw = INfcAdapter.Stub.asInterface(nfcServiceBinder);
310        int flags = enablePolling ? ENABLE_POLLING_FLAGS : DISABLE_POLLING_FLAGS;
311        if (DEBUG) Slog.v(TAG, "Setting NFC reader mode to flags " + flags);
312        try {
313            nfcAdapterRaw.setReaderMode(nfcInterfaceToken, null, flags, null);
314        } catch (RemoteException e) {
315            Slog.w(TAG, "Could not notify NFC service, remote exception: " + e);
316        }
317    }
318
319    private static int[] toArray(Collection<Integer> c) {
320        int len = c.size();
321        int[] ret = new int[len];
322        int idx = 0;
323        for (Integer i : c) {
324            ret[idx++] = i;
325        }
326        return ret;
327    }
328
329    private static String cameraStateToString(int newCameraState) {
330        switch (newCameraState) {
331            case CAMERA_STATE_OPEN: return "CAMERA_STATE_OPEN";
332            case CAMERA_STATE_ACTIVE: return "CAMERA_STATE_ACTIVE";
333            case CAMERA_STATE_IDLE: return "CAMERA_STATE_IDLE";
334            case CAMERA_STATE_CLOSED: return "CAMERA_STATE_CLOSED";
335            default: break;
336        }
337        return "CAMERA_STATE_UNKNOWN";
338    }
339}
340