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.content.BroadcastReceiver; 19 import android.content.Context; 20 import android.content.Intent; 21 import android.content.IntentFilter; 22 import android.hardware.ICameraService; 23 import android.hardware.ICameraServiceProxy; 24 import android.metrics.LogMaker; 25 import android.nfc.INfcAdapter; 26 import android.os.Binder; 27 import android.os.Handler; 28 import android.os.IBinder; 29 import android.os.Message; 30 import android.os.Process; 31 import android.os.RemoteException; 32 import android.os.SystemClock; 33 import android.os.SystemProperties; 34 import android.os.UserManager; 35 import android.util.ArrayMap; 36 import android.util.ArraySet; 37 import android.util.Slog; 38 39 import com.android.internal.logging.MetricsLogger; 40 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 41 import com.android.server.LocalServices; 42 import com.android.server.ServiceThread; 43 import com.android.server.SystemService; 44 45 import java.util.ArrayList; 46 import java.util.Collection; 47 import java.util.Collections; 48 import java.util.List; 49 import java.util.Set; 50 51 /** 52 * CameraServiceProxy is the system_server analog to the camera service running in mediaserver. 53 * 54 * @hide 55 */ 56 public class CameraServiceProxy extends SystemService 57 implements Handler.Callback, IBinder.DeathRecipient { 58 private static final String TAG = "CameraService_proxy"; 59 private static final boolean DEBUG = false; 60 61 /** 62 * This must match the ICameraService.aidl definition 63 */ 64 private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera"; 65 66 public static final String CAMERA_SERVICE_PROXY_BINDER_NAME = "media.camera.proxy"; 67 68 // Flags arguments to NFC adapter to enable/disable NFC 69 public static final int DISABLE_POLLING_FLAGS = 0x1000; 70 public static final int ENABLE_POLLING_FLAGS = 0x0000; 71 72 // Handler message codes 73 private static final int MSG_SWITCH_USER = 1; 74 75 private static final int RETRY_DELAY_TIME = 20; //ms 76 77 // Maximum entries to keep in usage history before dumping out 78 private static final int MAX_USAGE_HISTORY = 100; 79 80 private final Context mContext; 81 private final ServiceThread mHandlerThread; 82 private final Handler mHandler; 83 private UserManager mUserManager; 84 85 private final Object mLock = new Object(); 86 private Set<Integer> mEnabledCameraUsers; 87 private int mLastUser; 88 89 private ICameraService mCameraServiceRaw; 90 91 private final ArrayMap<String, CameraUsageEvent> mActiveCameraUsage = new ArrayMap<>(); 92 private final List<CameraUsageEvent> mCameraUsageHistory = new ArrayList<>(); 93 private final MetricsLogger mLogger = new MetricsLogger(); 94 private static final String NFC_NOTIFICATION_PROP = "ro.camera.notify_nfc"; 95 private static final String NFC_SERVICE_BINDER_NAME = "nfc"; 96 private static final IBinder nfcInterfaceToken = new Binder(); 97 98 private final boolean mNotifyNfc; 99 100 /** 101 * Structure to track camera usage 102 */ 103 private static class CameraUsageEvent { 104 public final int mCameraFacing; 105 public final String mClientName; 106 public final int mAPILevel; 107 108 private boolean mCompleted; 109 private long mDurationOrStartTimeMs; // Either start time, or duration once completed 110 CameraUsageEvent(int facing, String clientName, int apiLevel)111 public CameraUsageEvent(int facing, String clientName, int apiLevel) { 112 mCameraFacing = facing; 113 mClientName = clientName; 114 mAPILevel = apiLevel; 115 mDurationOrStartTimeMs = SystemClock.elapsedRealtime(); 116 mCompleted = false; 117 } 118 markCompleted()119 public void markCompleted() { 120 if (mCompleted) { 121 return; 122 } 123 mCompleted = true; 124 mDurationOrStartTimeMs = SystemClock.elapsedRealtime() - mDurationOrStartTimeMs; 125 if (CameraServiceProxy.DEBUG) { 126 Slog.v(TAG, "A camera facing " + cameraFacingToString(mCameraFacing) + 127 " was in use by " + mClientName + " for " + 128 mDurationOrStartTimeMs + " ms"); 129 } 130 } 131 132 /** 133 * Return duration of camera usage event, or 0 if the event is not done 134 */ getDuration()135 public long getDuration() { 136 return mCompleted ? mDurationOrStartTimeMs : 0; 137 } 138 } 139 140 private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 141 @Override 142 public void onReceive(Context context, Intent intent) { 143 final String action = intent.getAction(); 144 if (action == null) return; 145 146 switch (action) { 147 case Intent.ACTION_USER_ADDED: 148 case Intent.ACTION_USER_REMOVED: 149 case Intent.ACTION_USER_INFO_CHANGED: 150 case Intent.ACTION_MANAGED_PROFILE_ADDED: 151 case Intent.ACTION_MANAGED_PROFILE_REMOVED: 152 synchronized(mLock) { 153 // Return immediately if we haven't seen any users start yet 154 if (mEnabledCameraUsers == null) return; 155 switchUserLocked(mLastUser); 156 } 157 break; 158 default: 159 break; // do nothing 160 } 161 162 } 163 }; 164 165 private final ICameraServiceProxy.Stub mCameraServiceProxy = new ICameraServiceProxy.Stub() { 166 @Override 167 public void pingForUserUpdate() { 168 if (Binder.getCallingUid() != Process.CAMERASERVER_UID) { 169 Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() + " doesn't match expected " + 170 " camera service UID!"); 171 return; 172 } 173 notifySwitchWithRetries(30); 174 } 175 176 @Override 177 public void notifyCameraState(String cameraId, int newCameraState, int facing, 178 String clientName, int apiLevel) { 179 if (Binder.getCallingUid() != Process.CAMERASERVER_UID) { 180 Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() + " doesn't match expected " + 181 " camera service UID!"); 182 return; 183 } 184 String state = cameraStateToString(newCameraState); 185 String facingStr = cameraFacingToString(facing); 186 if (DEBUG) Slog.v(TAG, "Camera " + cameraId + " facing " + facingStr + " state now " + 187 state + " for client " + clientName + " API Level " + apiLevel); 188 189 updateActivityCount(cameraId, newCameraState, facing, clientName, apiLevel); 190 } 191 }; 192 CameraServiceProxy(Context context)193 public CameraServiceProxy(Context context) { 194 super(context); 195 mContext = context; 196 mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_DISPLAY, /*allowTo*/false); 197 mHandlerThread.start(); 198 mHandler = new Handler(mHandlerThread.getLooper(), this); 199 200 mNotifyNfc = SystemProperties.getInt(NFC_NOTIFICATION_PROP, 0) > 0; 201 if (DEBUG) Slog.v(TAG, "Notify NFC behavior is " + (mNotifyNfc ? "active" : "disabled")); 202 } 203 204 @Override handleMessage(Message msg)205 public boolean handleMessage(Message msg) { 206 switch(msg.what) { 207 case MSG_SWITCH_USER: { 208 notifySwitchWithRetries(msg.arg1); 209 } break; 210 default: { 211 Slog.e(TAG, "CameraServiceProxy error, invalid message: " + msg.what); 212 } break; 213 } 214 return true; 215 } 216 217 @Override onStart()218 public void onStart() { 219 mUserManager = UserManager.get(mContext); 220 if (mUserManager == null) { 221 // Should never see this unless someone messes up the SystemServer service boot order. 222 throw new IllegalStateException("UserManagerService must start before" + 223 " CameraServiceProxy!"); 224 } 225 226 IntentFilter filter = new IntentFilter(); 227 filter.addAction(Intent.ACTION_USER_ADDED); 228 filter.addAction(Intent.ACTION_USER_REMOVED); 229 filter.addAction(Intent.ACTION_USER_INFO_CHANGED); 230 filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED); 231 filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED); 232 mContext.registerReceiver(mIntentReceiver, filter); 233 234 publishBinderService(CAMERA_SERVICE_PROXY_BINDER_NAME, mCameraServiceProxy); 235 publishLocalService(CameraServiceProxy.class, this); 236 237 CameraStatsJobService.schedule(mContext); 238 } 239 240 @Override onStartUser(int userHandle)241 public void onStartUser(int userHandle) { 242 synchronized(mLock) { 243 if (mEnabledCameraUsers == null) { 244 // Initialize mediaserver, or update mediaserver if we are recovering from a crash. 245 switchUserLocked(userHandle); 246 } 247 } 248 } 249 250 @Override onSwitchUser(int userHandle)251 public void onSwitchUser(int userHandle) { 252 synchronized(mLock) { 253 switchUserLocked(userHandle); 254 } 255 } 256 257 /** 258 * Handle the death of the native camera service 259 */ 260 @Override binderDied()261 public void binderDied() { 262 if (DEBUG) Slog.w(TAG, "Native camera service has died"); 263 synchronized(mLock) { 264 mCameraServiceRaw = null; 265 266 // All cameras reset to idle on camera service death 267 boolean wasEmpty = mActiveCameraUsage.isEmpty(); 268 mActiveCameraUsage.clear(); 269 270 if ( mNotifyNfc && !wasEmpty ) { 271 notifyNfcService(/*enablePolling*/ true); 272 } 273 } 274 } 275 276 /** 277 * Dump camera usage events to log. 278 * Package-private 279 */ dumpUsageEvents()280 void dumpUsageEvents() { 281 synchronized(mLock) { 282 // Randomize order of events so that it's not meaningful 283 Collections.shuffle(mCameraUsageHistory); 284 for (CameraUsageEvent e : mCameraUsageHistory) { 285 if (DEBUG) { 286 Slog.v(TAG, "Camera: " + e.mClientName + " used a camera facing " + 287 cameraFacingToString(e.mCameraFacing) + " for " + 288 e.getDuration() + " ms"); 289 } 290 int subtype = 0; 291 switch(e.mCameraFacing) { 292 case ICameraServiceProxy.CAMERA_FACING_BACK: 293 subtype = MetricsEvent.CAMERA_BACK_USED; 294 break; 295 case ICameraServiceProxy.CAMERA_FACING_FRONT: 296 subtype = MetricsEvent.CAMERA_FRONT_USED; 297 break; 298 case ICameraServiceProxy.CAMERA_FACING_EXTERNAL: 299 subtype = MetricsEvent.CAMERA_EXTERNAL_USED; 300 break; 301 default: 302 continue; 303 } 304 LogMaker l = new LogMaker(MetricsEvent.ACTION_CAMERA_EVENT) 305 .setType(MetricsEvent.TYPE_ACTION) 306 .setSubtype(subtype) 307 .setLatency(e.getDuration()) 308 .addTaggedData(MetricsEvent.FIELD_CAMERA_API_LEVEL, e.mAPILevel) 309 .setPackageName(e.mClientName); 310 mLogger.write(l); 311 } 312 mCameraUsageHistory.clear(); 313 } 314 final long ident = Binder.clearCallingIdentity(); 315 try { 316 CameraStatsJobService.schedule(mContext); 317 } finally { 318 Binder.restoreCallingIdentity(ident); 319 } 320 } 321 switchUserLocked(int userHandle)322 private void switchUserLocked(int userHandle) { 323 Set<Integer> currentUserHandles = getEnabledUserHandles(userHandle); 324 mLastUser = userHandle; 325 if (mEnabledCameraUsers == null || !mEnabledCameraUsers.equals(currentUserHandles)) { 326 // Some user handles have been added or removed, update mediaserver. 327 mEnabledCameraUsers = currentUserHandles; 328 notifyMediaserverLocked(ICameraService.EVENT_USER_SWITCHED, currentUserHandles); 329 } 330 } 331 getEnabledUserHandles(int currentUserHandle)332 private Set<Integer> getEnabledUserHandles(int currentUserHandle) { 333 int[] userProfiles = mUserManager.getEnabledProfileIds(currentUserHandle); 334 Set<Integer> handles = new ArraySet<>(userProfiles.length); 335 336 for (int id : userProfiles) { 337 handles.add(id); 338 } 339 340 return handles; 341 } 342 notifySwitchWithRetries(int retries)343 private void notifySwitchWithRetries(int retries) { 344 synchronized(mLock) { 345 if (mEnabledCameraUsers == null) { 346 return; 347 } 348 if (notifyMediaserverLocked(ICameraService.EVENT_USER_SWITCHED, mEnabledCameraUsers)) { 349 retries = 0; 350 } 351 } 352 if (retries <= 0) { 353 return; 354 } 355 Slog.i(TAG, "Could not notify camera service of user switch, retrying..."); 356 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SWITCH_USER, retries - 1, 0, null), 357 RETRY_DELAY_TIME); 358 } 359 notifyMediaserverLocked(int eventType, Set<Integer> updatedUserHandles)360 private boolean notifyMediaserverLocked(int eventType, Set<Integer> updatedUserHandles) { 361 // Forward the user switch event to the native camera service running in the mediaserver 362 // process. 363 if (mCameraServiceRaw == null) { 364 IBinder cameraServiceBinder = getBinderService(CAMERA_SERVICE_BINDER_NAME); 365 if (cameraServiceBinder == null) { 366 Slog.w(TAG, "Could not notify mediaserver, camera service not available."); 367 return false; // Camera service not active, cannot evict user clients. 368 } 369 try { 370 cameraServiceBinder.linkToDeath(this, /*flags*/ 0); 371 } catch (RemoteException e) { 372 Slog.w(TAG, "Could not link to death of native camera service"); 373 return false; 374 } 375 376 mCameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder); 377 } 378 379 try { 380 mCameraServiceRaw.notifySystemEvent(eventType, toArray(updatedUserHandles)); 381 } catch (RemoteException e) { 382 Slog.w(TAG, "Could not notify mediaserver, remote exception: " + e); 383 // Not much we can do if camera service is dead. 384 return false; 385 } 386 return true; 387 } 388 updateActivityCount(String cameraId, int newCameraState, int facing, String clientName, int apiLevel)389 private void updateActivityCount(String cameraId, int newCameraState, int facing, 390 String clientName, int apiLevel) { 391 synchronized(mLock) { 392 // Update active camera list and notify NFC if necessary 393 boolean wasEmpty = mActiveCameraUsage.isEmpty(); 394 switch (newCameraState) { 395 case ICameraServiceProxy.CAMERA_STATE_OPEN: 396 break; 397 case ICameraServiceProxy.CAMERA_STATE_ACTIVE: 398 CameraUsageEvent newEvent = new CameraUsageEvent(facing, clientName, apiLevel); 399 CameraUsageEvent oldEvent = mActiveCameraUsage.put(cameraId, newEvent); 400 if (oldEvent != null) { 401 Slog.w(TAG, "Camera " + cameraId + " was already marked as active"); 402 oldEvent.markCompleted(); 403 mCameraUsageHistory.add(oldEvent); 404 } 405 break; 406 case ICameraServiceProxy.CAMERA_STATE_IDLE: 407 case ICameraServiceProxy.CAMERA_STATE_CLOSED: 408 CameraUsageEvent doneEvent = mActiveCameraUsage.remove(cameraId); 409 if (doneEvent != null) { 410 doneEvent.markCompleted(); 411 mCameraUsageHistory.add(doneEvent); 412 if (mCameraUsageHistory.size() > MAX_USAGE_HISTORY) { 413 dumpUsageEvents(); 414 } 415 } 416 break; 417 } 418 boolean isEmpty = mActiveCameraUsage.isEmpty(); 419 if ( mNotifyNfc && (wasEmpty != isEmpty) ) { 420 notifyNfcService(isEmpty); 421 } 422 } 423 } 424 notifyNfcService(boolean enablePolling)425 private void notifyNfcService(boolean enablePolling) { 426 427 IBinder nfcServiceBinder = getBinderService(NFC_SERVICE_BINDER_NAME); 428 if (nfcServiceBinder == null) { 429 Slog.w(TAG, "Could not connect to NFC service to notify it of camera state"); 430 return; 431 } 432 INfcAdapter nfcAdapterRaw = INfcAdapter.Stub.asInterface(nfcServiceBinder); 433 int flags = enablePolling ? ENABLE_POLLING_FLAGS : DISABLE_POLLING_FLAGS; 434 if (DEBUG) Slog.v(TAG, "Setting NFC reader mode to flags " + flags); 435 try { 436 nfcAdapterRaw.setReaderMode(nfcInterfaceToken, null, flags, null); 437 } catch (RemoteException e) { 438 Slog.w(TAG, "Could not notify NFC service, remote exception: " + e); 439 } 440 } 441 toArray(Collection<Integer> c)442 private static int[] toArray(Collection<Integer> c) { 443 int len = c.size(); 444 int[] ret = new int[len]; 445 int idx = 0; 446 for (Integer i : c) { 447 ret[idx++] = i; 448 } 449 return ret; 450 } 451 cameraStateToString(int newCameraState)452 private static String cameraStateToString(int newCameraState) { 453 switch (newCameraState) { 454 case ICameraServiceProxy.CAMERA_STATE_OPEN: return "CAMERA_STATE_OPEN"; 455 case ICameraServiceProxy.CAMERA_STATE_ACTIVE: return "CAMERA_STATE_ACTIVE"; 456 case ICameraServiceProxy.CAMERA_STATE_IDLE: return "CAMERA_STATE_IDLE"; 457 case ICameraServiceProxy.CAMERA_STATE_CLOSED: return "CAMERA_STATE_CLOSED"; 458 default: break; 459 } 460 return "CAMERA_STATE_UNKNOWN"; 461 } 462 cameraFacingToString(int cameraFacing)463 private static String cameraFacingToString(int cameraFacing) { 464 switch (cameraFacing) { 465 case ICameraServiceProxy.CAMERA_FACING_BACK: return "CAMERA_FACING_BACK"; 466 case ICameraServiceProxy.CAMERA_FACING_FRONT: return "CAMERA_FACING_FRONT"; 467 case ICameraServiceProxy.CAMERA_FACING_EXTERNAL: return "CAMERA_FACING_EXTERNAL"; 468 default: break; 469 } 470 return "CAMERA_FACING_UNKNOWN"; 471 } 472 473 } 474