Merge changes from topic 'camera-tron' into oc-dr1-dev
am: 280165118d
Change-Id: Ic163d728471cb1cdb7dfc60955452a0f1491dc76
This commit is contained in:
@@ -3821,6 +3821,11 @@
|
||||
<service android:name="com.android.server.PreloadsFileCacheExpirationJobService"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE" >
|
||||
</service>
|
||||
|
||||
<service android:name="com.android.server.camera.CameraStatsJobService"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE" >
|
||||
</service>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
@@ -138,6 +138,18 @@ message MetricsEvent {
|
||||
REASON_TIMEOUT = 19;
|
||||
}
|
||||
|
||||
// Subtypes of camera events for ACTION_CAMERA_EVENT
|
||||
enum CameraEvent {
|
||||
// A back-facing camera was used
|
||||
CAMERA_BACK_USED = 0;
|
||||
|
||||
// A front-facing camera was used
|
||||
CAMERA_FRONT_USED = 1;
|
||||
|
||||
// An external camera was used
|
||||
CAMERA_EXTERNAL_USED = 2;
|
||||
}
|
||||
|
||||
// Known visual elements: views or controls.
|
||||
enum View {
|
||||
// Unknown view
|
||||
@@ -4196,6 +4208,12 @@ message MetricsEvent {
|
||||
// OS: O DR
|
||||
DIALOG_BLUETOOTH_PAIRED_DEVICE_FORGET = 1031;
|
||||
|
||||
// An event from the camera service
|
||||
// CATEGORY: OTHER
|
||||
// SUBTYPE: CameraEvent
|
||||
// OS: O DR
|
||||
ACTION_CAMERA_EVENT = 1032;
|
||||
|
||||
// ---- End O-DR1 Constants, all O-DR1 constants go above this line ----
|
||||
|
||||
// Add new aosp constants above this line.
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.hardware.ICameraService;
|
||||
import android.hardware.ICameraServiceProxy;
|
||||
import android.metrics.LogMaker;
|
||||
import android.nfc.INfcAdapter;
|
||||
import android.os.Binder;
|
||||
import android.os.Handler;
|
||||
@@ -28,15 +29,23 @@ import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.UserManager;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.ServiceThread;
|
||||
import com.android.server.SystemService;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -65,6 +74,9 @@ public class CameraServiceProxy extends SystemService
|
||||
|
||||
private static final int RETRY_DELAY_TIME = 20; //ms
|
||||
|
||||
// Maximum entries to keep in usage history before dumping out
|
||||
private static final int MAX_USAGE_HISTORY = 100;
|
||||
|
||||
private final Context mContext;
|
||||
private final ServiceThread mHandlerThread;
|
||||
private final Handler mHandler;
|
||||
@@ -76,14 +88,52 @@ public class CameraServiceProxy extends SystemService
|
||||
|
||||
private ICameraService mCameraServiceRaw;
|
||||
|
||||
private final ArraySet<String> mActiveCameraIds = new ArraySet<>();
|
||||
|
||||
private final ArrayMap<String, CameraUsageEvent> mActiveCameraUsage = new ArrayMap<>();
|
||||
private final List<CameraUsageEvent> mCameraUsageHistory = new ArrayList<>();
|
||||
private final MetricsLogger mLogger = new MetricsLogger();
|
||||
private static final String NFC_NOTIFICATION_PROP = "ro.camera.notify_nfc";
|
||||
private static final String NFC_SERVICE_BINDER_NAME = "nfc";
|
||||
private static final IBinder nfcInterfaceToken = new Binder();
|
||||
|
||||
private final boolean mNotifyNfc;
|
||||
private int mActiveCameraCount = 0;
|
||||
|
||||
/**
|
||||
* Structure to track camera usage
|
||||
*/
|
||||
private static class CameraUsageEvent {
|
||||
public final int mCameraFacing;
|
||||
public final String mClientName;
|
||||
|
||||
private boolean mCompleted;
|
||||
private long mDurationOrStartTimeMs; // Either start time, or duration once completed
|
||||
|
||||
public CameraUsageEvent(int facing, String clientName) {
|
||||
mCameraFacing = facing;
|
||||
mClientName = clientName;
|
||||
mDurationOrStartTimeMs = SystemClock.elapsedRealtime();
|
||||
mCompleted = false;
|
||||
}
|
||||
|
||||
public void markCompleted() {
|
||||
if (mCompleted) {
|
||||
return;
|
||||
}
|
||||
mCompleted = true;
|
||||
mDurationOrStartTimeMs = SystemClock.elapsedRealtime() - mDurationOrStartTimeMs;
|
||||
if (CameraServiceProxy.DEBUG) {
|
||||
Slog.v(TAG, "A camera facing " + cameraFacingToString(mCameraFacing) +
|
||||
" was in use by " + mClientName + " for " +
|
||||
mDurationOrStartTimeMs + " ms");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return duration of camera usage event, or 0 if the event is not done
|
||||
*/
|
||||
public long getDuration() {
|
||||
return mCompleted ? mDurationOrStartTimeMs : 0;
|
||||
}
|
||||
}
|
||||
|
||||
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
@@ -120,10 +170,11 @@ public class CameraServiceProxy extends SystemService
|
||||
public void notifyCameraState(String cameraId, int newCameraState, int facing,
|
||||
String clientName) {
|
||||
String state = cameraStateToString(newCameraState);
|
||||
if (DEBUG) Slog.v(TAG, "Camera " + cameraId + " facing " + facing + " state now " +
|
||||
String facingStr = cameraFacingToString(facing);
|
||||
if (DEBUG) Slog.v(TAG, "Camera " + cameraId + " facing " + facingStr + " state now " +
|
||||
state + " for client " + clientName);
|
||||
|
||||
updateActivityCount(cameraId, newCameraState);
|
||||
updateActivityCount(cameraId, newCameraState, facing, clientName);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -169,6 +220,9 @@ public class CameraServiceProxy extends SystemService
|
||||
mContext.registerReceiver(mIntentReceiver, filter);
|
||||
|
||||
publishBinderService(CAMERA_SERVICE_PROXY_BINDER_NAME, mCameraServiceProxy);
|
||||
publishLocalService(CameraServiceProxy.class, this);
|
||||
|
||||
CameraStatsJobService.schedule(mContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -198,8 +252,8 @@ public class CameraServiceProxy extends SystemService
|
||||
mCameraServiceRaw = null;
|
||||
|
||||
// All cameras reset to idle on camera service death
|
||||
boolean wasEmpty = mActiveCameraIds.isEmpty();
|
||||
mActiveCameraIds.clear();
|
||||
boolean wasEmpty = mActiveCameraUsage.isEmpty();
|
||||
mActiveCameraUsage.clear();
|
||||
|
||||
if ( mNotifyNfc && !wasEmpty ) {
|
||||
notifyNfcService(/*enablePolling*/ true);
|
||||
@@ -207,6 +261,46 @@ public class CameraServiceProxy extends SystemService
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump camera usage events to log.
|
||||
* Package-private
|
||||
*/
|
||||
void dumpUsageEvents() {
|
||||
synchronized(mLock) {
|
||||
// Randomize order of events so that it's not meaningful
|
||||
Collections.shuffle(mCameraUsageHistory);
|
||||
for (CameraUsageEvent e : mCameraUsageHistory) {
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, "Camera: " + e.mClientName + " used a camera facing " +
|
||||
cameraFacingToString(e.mCameraFacing) + " for " +
|
||||
e.getDuration() + " ms");
|
||||
}
|
||||
int subtype = 0;
|
||||
switch(e.mCameraFacing) {
|
||||
case ICameraServiceProxy.CAMERA_FACING_BACK:
|
||||
subtype = MetricsEvent.CAMERA_BACK_USED;
|
||||
break;
|
||||
case ICameraServiceProxy.CAMERA_FACING_FRONT:
|
||||
subtype = MetricsEvent.CAMERA_FRONT_USED;
|
||||
break;
|
||||
case ICameraServiceProxy.CAMERA_FACING_EXTERNAL:
|
||||
subtype = MetricsEvent.CAMERA_EXTERNAL_USED;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
LogMaker l = new LogMaker(MetricsEvent.ACTION_CAMERA_EVENT)
|
||||
.setType(MetricsEvent.TYPE_ACTION)
|
||||
.setSubtype(subtype)
|
||||
.setLatency(e.getDuration())
|
||||
.setPackageName(e.mClientName);
|
||||
mLogger.write(l);
|
||||
}
|
||||
mCameraUsageHistory.clear();
|
||||
}
|
||||
CameraStatsJobService.schedule(mContext);
|
||||
}
|
||||
|
||||
private void switchUserLocked(int userHandle) {
|
||||
Set<Integer> currentUserHandles = getEnabledUserHandles(userHandle);
|
||||
mLastUser = userHandle;
|
||||
@@ -274,21 +368,35 @@ public class CameraServiceProxy extends SystemService
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateActivityCount(String cameraId, int newCameraState) {
|
||||
private void updateActivityCount(String cameraId, int newCameraState, int facing, String clientName) {
|
||||
synchronized(mLock) {
|
||||
boolean wasEmpty = mActiveCameraIds.isEmpty();
|
||||
// Update active camera list and notify NFC if necessary
|
||||
boolean wasEmpty = mActiveCameraUsage.isEmpty();
|
||||
switch (newCameraState) {
|
||||
case ICameraServiceProxy.CAMERA_STATE_OPEN:
|
||||
break;
|
||||
case ICameraServiceProxy.CAMERA_STATE_ACTIVE:
|
||||
mActiveCameraIds.add(cameraId);
|
||||
CameraUsageEvent newEvent = new CameraUsageEvent(facing, clientName);
|
||||
CameraUsageEvent oldEvent = mActiveCameraUsage.put(cameraId, newEvent);
|
||||
if (oldEvent != null) {
|
||||
Slog.w(TAG, "Camera " + cameraId + " was already marked as active");
|
||||
oldEvent.markCompleted();
|
||||
mCameraUsageHistory.add(oldEvent);
|
||||
}
|
||||
break;
|
||||
case ICameraServiceProxy.CAMERA_STATE_IDLE:
|
||||
case ICameraServiceProxy.CAMERA_STATE_CLOSED:
|
||||
mActiveCameraIds.remove(cameraId);
|
||||
CameraUsageEvent doneEvent = mActiveCameraUsage.remove(cameraId);
|
||||
if (doneEvent != null) {
|
||||
doneEvent.markCompleted();
|
||||
mCameraUsageHistory.add(doneEvent);
|
||||
if (mCameraUsageHistory.size() > MAX_USAGE_HISTORY) {
|
||||
dumpUsageEvents();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
boolean isEmpty = mActiveCameraIds.isEmpty();
|
||||
boolean isEmpty = mActiveCameraUsage.isEmpty();
|
||||
if ( mNotifyNfc && (wasEmpty != isEmpty) ) {
|
||||
notifyNfcService(isEmpty);
|
||||
}
|
||||
@@ -332,4 +440,15 @@ public class CameraServiceProxy extends SystemService
|
||||
}
|
||||
return "CAMERA_STATE_UNKNOWN";
|
||||
}
|
||||
|
||||
private static String cameraFacingToString(int cameraFacing) {
|
||||
switch (cameraFacing) {
|
||||
case ICameraServiceProxy.CAMERA_FACING_BACK: return "CAMERA_FACING_BACK";
|
||||
case ICameraServiceProxy.CAMERA_FACING_FRONT: return "CAMERA_FACING_FRONT";
|
||||
case ICameraServiceProxy.CAMERA_FACING_EXTERNAL: return "CAMERA_FACING_EXTERNAL";
|
||||
default: break;
|
||||
}
|
||||
return "CAMERA_FACING_UNKNOWN";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package com.android.server.camera;
|
||||
|
||||
import android.app.job.JobInfo;
|
||||
import android.app.job.JobParameters;
|
||||
import android.app.job.JobScheduler;
|
||||
import android.app.job.JobService;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.util.Slog;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.android.server.LocalServices;
|
||||
|
||||
/**
|
||||
* A JobService to periodically collect camera usage stats.
|
||||
*/
|
||||
public class CameraStatsJobService extends JobService {
|
||||
private static final String TAG = "CameraStatsJobService";
|
||||
|
||||
// Must be unique within UID (system service)
|
||||
private static final int CAMERA_REPORTING_JOB_ID = 0xCA3E7A;
|
||||
|
||||
private static ComponentName sCameraStatsJobServiceName = new ComponentName(
|
||||
"android",
|
||||
CameraStatsJobService.class.getName());
|
||||
|
||||
@Override
|
||||
public boolean onStartJob(JobParameters params) {
|
||||
CameraServiceProxy serviceProxy = LocalServices.getService(CameraServiceProxy.class);
|
||||
if (serviceProxy == null) {
|
||||
Slog.w(TAG, "Can't collect camera usage stats - no camera service proxy found");
|
||||
return false;
|
||||
}
|
||||
|
||||
serviceProxy.dumpUsageEvents();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onStopJob(JobParameters params) {
|
||||
// All work is done in onStartJob, so nothing to stop here
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void schedule(Context context) {
|
||||
|
||||
JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
|
||||
if (js == null) {
|
||||
Slog.e(TAG, "Can't collect camera usage stats - no Job Scheduler");
|
||||
return;
|
||||
}
|
||||
js.schedule(new JobInfo.Builder(CAMERA_REPORTING_JOB_ID, sCameraStatsJobServiceName)
|
||||
.setMinimumLatency(TimeUnit.DAYS.toMillis(1))
|
||||
.setRequiresDeviceIdle(true)
|
||||
.build());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -760,13 +760,6 @@ public final class SystemServer {
|
||||
|
||||
mContentResolver = context.getContentResolver();
|
||||
|
||||
if (!disableCameraService) {
|
||||
Slog.i(TAG, "Camera Service Proxy");
|
||||
traceBeginAndSlog("StartCameraServiceProxy");
|
||||
mSystemServiceManager.startService(CameraServiceProxy.class);
|
||||
traceEnd();
|
||||
}
|
||||
|
||||
// The AccountManager must come before the ContentService
|
||||
traceBeginAndSlog("StartAccountManagerService");
|
||||
mSystemServiceManager.startService(ACCOUNT_SERVICE_CLASS);
|
||||
@@ -1501,6 +1494,12 @@ public final class SystemServer {
|
||||
}
|
||||
}
|
||||
|
||||
if (!disableCameraService) {
|
||||
traceBeginAndSlog("StartCameraServiceProxy");
|
||||
mSystemServiceManager.startService(CameraServiceProxy.class);
|
||||
traceEnd();
|
||||
}
|
||||
|
||||
// Before things start rolling, be sure we have decided whether
|
||||
// we are in safe mode.
|
||||
final boolean safeMode = wm.detectSafeMode();
|
||||
|
||||
Reference in New Issue
Block a user