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 * CameraService is the system_server analog to the camera service running in mediaserver.
44 *
45 * @hide
46 */
47public class CameraService 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 CameraService(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, "CameraService 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 CameraService!");
164        }
165
166        IntentFilter filter = new IntentFilter();
167        filter.addAction(Intent.ACTION_USER_ADDED);
168        filter.addAction(Intent.ACTION_USER_REMOVED);
169        filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
170        filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
171        filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
172        mContext.registerReceiver(mIntentReceiver, filter);
173
174        publishBinderService(CAMERA_SERVICE_PROXY_BINDER_NAME, mCameraServiceProxy);
175    }
176
177    @Override
178    public void onStartUser(int userHandle) {
179        synchronized(mLock) {
180            if (mEnabledCameraUsers == null) {
181                // Initialize mediaserver, or update mediaserver if we are recovering from a crash.
182                switchUserLocked(userHandle);
183            }
184        }
185    }
186
187    @Override
188    public void onSwitchUser(int userHandle) {
189        synchronized(mLock) {
190            switchUserLocked(userHandle);
191        }
192    }
193
194    /**
195     * Handle the death of the native camera service
196     */
197    @Override
198    public void binderDied() {
199        if (DEBUG) Slog.w(TAG, "Native camera service has died");
200        synchronized(mLock) {
201            mCameraServiceRaw = null;
202
203            // All cameras reset to idle on camera service death
204            boolean wasEmpty = mActiveCameraIds.isEmpty();
205            mActiveCameraIds.clear();
206
207            if ( mNotifyNfc && !wasEmpty ) {
208                notifyNfcService(/*enablePolling*/ true);
209            }
210        }
211    }
212
213    private void switchUserLocked(int userHandle) {
214        Set<Integer> currentUserHandles = getEnabledUserHandles(userHandle);
215        mLastUser = userHandle;
216        if (mEnabledCameraUsers == null || !mEnabledCameraUsers.equals(currentUserHandles)) {
217            // Some user handles have been added or removed, update mediaserver.
218            mEnabledCameraUsers = currentUserHandles;
219            notifyMediaserverLocked(ICameraService.EVENT_USER_SWITCHED, currentUserHandles);
220        }
221    }
222
223    private Set<Integer> getEnabledUserHandles(int currentUserHandle) {
224        int[] userProfiles = mUserManager.getEnabledProfileIds(currentUserHandle);
225        Set<Integer> handles = new ArraySet<>(userProfiles.length);
226
227        for (int id : userProfiles) {
228            handles.add(id);
229        }
230
231        return handles;
232    }
233
234    private void notifySwitchWithRetries(int retries) {
235        synchronized(mLock) {
236            if (mEnabledCameraUsers == null) {
237                return;
238            }
239            if (notifyMediaserverLocked(ICameraService.EVENT_USER_SWITCHED, mEnabledCameraUsers)) {
240                retries = 0;
241            }
242        }
243        if (retries <= 0) {
244            return;
245        }
246        Slog.i(TAG, "Could not notify camera service of user switch, retrying...");
247        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SWITCH_USER, retries - 1, 0, null),
248                RETRY_DELAY_TIME);
249    }
250
251    private boolean notifyMediaserverLocked(int eventType, Set<Integer> updatedUserHandles) {
252        // Forward the user switch event to the native camera service running in the mediaserver
253        // process.
254        if (mCameraServiceRaw == null) {
255            IBinder cameraServiceBinder = getBinderService(CAMERA_SERVICE_BINDER_NAME);
256            if (cameraServiceBinder == null) {
257                Slog.w(TAG, "Could not notify mediaserver, camera service not available.");
258                return false; // Camera service not active, cannot evict user clients.
259            }
260            try {
261                cameraServiceBinder.linkToDeath(this, /*flags*/ 0);
262            } catch (RemoteException e) {
263                Slog.w(TAG, "Could not link to death of native camera service");
264                return false;
265            }
266
267            mCameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
268        }
269
270        try {
271            mCameraServiceRaw.notifySystemEvent(eventType, toArray(updatedUserHandles));
272        } catch (RemoteException e) {
273            Slog.w(TAG, "Could not notify mediaserver, remote exception: " + e);
274            // Not much we can do if camera service is dead.
275            return false;
276        }
277        return true;
278    }
279
280    private void updateActivityCount(String cameraId, int newCameraState) {
281        synchronized(mLock) {
282            boolean wasEmpty = mActiveCameraIds.isEmpty();
283            switch (newCameraState) {
284                case CAMERA_STATE_OPEN:
285                    break;
286                case CAMERA_STATE_ACTIVE:
287                    mActiveCameraIds.add(cameraId);
288                    break;
289                case CAMERA_STATE_IDLE:
290                case CAMERA_STATE_CLOSED:
291                    mActiveCameraIds.remove(cameraId);
292                    break;
293            }
294            boolean isEmpty = mActiveCameraIds.isEmpty();
295            if ( mNotifyNfc && (wasEmpty != isEmpty) ) {
296                notifyNfcService(isEmpty);
297            }
298        }
299    }
300
301    private void notifyNfcService(boolean enablePolling) {
302
303        IBinder nfcServiceBinder = getBinderService(NFC_SERVICE_BINDER_NAME);
304        if (nfcServiceBinder == null) {
305            Slog.w(TAG, "Could not connect to NFC service to notify it of camera state");
306            return;
307        }
308        INfcAdapter nfcAdapterRaw = INfcAdapter.Stub.asInterface(nfcServiceBinder);
309        int flags = enablePolling ? ENABLE_POLLING_FLAGS : DISABLE_POLLING_FLAGS;
310        if (DEBUG) Slog.v(TAG, "Setting NFC reader mode to flags " + flags);
311        try {
312            nfcAdapterRaw.setReaderMode(nfcInterfaceToken, null, flags, null);
313        } catch (RemoteException e) {
314            Slog.w(TAG, "Could not notify NFC service, remote exception: " + e);
315        }
316    }
317
318    private static int[] toArray(Collection<Integer> c) {
319        int len = c.size();
320        int[] ret = new int[len];
321        int idx = 0;
322        for (Integer i : c) {
323            ret[idx++] = i;
324        }
325        return ret;
326    }
327
328    private static String cameraStateToString(int newCameraState) {
329        switch (newCameraState) {
330            case CAMERA_STATE_OPEN: return "CAMERA_STATE_OPEN";
331            case CAMERA_STATE_ACTIVE: return "CAMERA_STATE_ACTIVE";
332            case CAMERA_STATE_IDLE: return "CAMERA_STATE_IDLE";
333            case CAMERA_STATE_CLOSED: return "CAMERA_STATE_CLOSED";
334            default: break;
335        }
336        return "CAMERA_STATE_UNKNOWN";
337    }
338}
339