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