Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fc6d56bd authored by Android Build Merger (Role)'s avatar Android Build Merger (Role) Committed by Android (Google) Code Review
Browse files

Merge "Merge changes from topic 'camera-tron' into oc-dr1-dev am: 28016511 am: 7dcff07a"

parents fe77678e 36b92684
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -3843,6 +3843,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>
+18 −0
Original line number Diff line number Diff line
@@ -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 ----

    // ACTION: Settings > Network & Internet > Mobile network > Mobile data
+131 −12
Original line number Diff line number Diff 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";
    }

}
+77 −0
Original line number Diff line number Diff line
/*
 * 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());

    }

}
+6 −7
Original line number Diff line number Diff line
@@ -763,13 +763,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);
@@ -1516,6 +1509,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();