1dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling/*
2dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * Copyright (C) 2014 The Android Open Source Project
3dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling *
4dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * Licensed under the Apache License, Version 2.0 (the "License");
5dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * you may not use this file except in compliance with the License.
6dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * You may obtain a copy of the License at
7dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling *
8dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling *      http://www.apache.org/licenses/LICENSE-2.0
9dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling *
10dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * Unless required by applicable law or agreed to in writing, software
11dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * distributed under the License is distributed on an "AS IS" BASIS,
12dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * See the License for the specific language governing permissions and
14dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * limitations under the License.
15dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling */
16dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
17dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberlingpackage com.android.camera.processing;
18dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
194a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberlingimport android.app.Notification;
204a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberlingimport android.app.NotificationManager;
21dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberlingimport android.app.Service;
2258acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberlingimport android.content.BroadcastReceiver;
23dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberlingimport android.content.Context;
24dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberlingimport android.content.Intent;
2558acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberlingimport android.content.IntentFilter;
26dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberlingimport android.os.IBinder;
27dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberlingimport android.os.PowerManager;
28dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberlingimport android.os.PowerManager.WakeLock;
29dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberlingimport android.os.Process;
3058acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberlingimport android.support.v4.content.LocalBroadcastManager;
31dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
32dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberlingimport com.android.camera.app.CameraApp;
33dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberlingimport com.android.camera.app.CameraServices;
345596b4c902dcb685928b43678f428746ca5ffd08Angus Kongimport com.android.camera.debug.Log;
352a978f2b7b07ce4e5f6163910912e79fed38aff8Seth Raphaelimport com.android.camera.session.CaptureSession;
364a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberlingimport com.android.camera.session.CaptureSession.ProgressListener;
37dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberlingimport com.android.camera.session.CaptureSessionManager;
384a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberlingimport com.android.camera2.R;
39dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
4058acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberlingimport java.util.concurrent.locks.Lock;
4158acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberlingimport java.util.concurrent.locks.ReentrantLock;
4258acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling
43dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling/**
44dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * A service that processes a {@code ProcessingTask}. The service uses a fifo
45dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * queue so that only one {@code ProcessingTask} is processed at a time.
46dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * <p>
47dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * The service is meant to be called via {@code ProcessingService.addTask},
48dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * which takes care of starting the service and enqueueing the
49dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * {@code ProcessingTask} task:
50dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling *
51dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * <pre>
52dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * {@code
53dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * ProcessingTask task = new MyProcessingTask(...);
54dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * ProcessingService.addTask(task);
55dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * }
56dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling * </pre>
57dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling */
584a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberlingpublic class ProcessingService extends Service implements ProgressListener {
5958acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling    /**
6058acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling     * Class used to receive broadcast and control the service accordingly.
6158acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling     */
6258acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling    public class ServiceController extends BroadcastReceiver {
6358acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling        @Override
6458acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling        public void onReceive(Context context, Intent intent) {
6558acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling            if (intent.getAction() == ACTION_PAUSE_PROCESSING_SERVICE) {
6658acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling                ProcessingService.this.pause();
6758acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling            } else if (intent.getAction() == ACTION_RESUME_PROCESSING_SERVICE) {
6858acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling                ProcessingService.this.resume();
6958acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling            }
7058acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling        }
7158acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling    }
7258acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling
735596b4c902dcb685928b43678f428746ca5ffd08Angus Kong    private static final Log.Tag TAG = new Log.Tag("ProcessingService");
74dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    private static final int THREAD_PRIORITY = Process.THREAD_PRIORITY_DISPLAY;
754a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling    private static final int CAMERA_NOTIFICATION_ID = 2;
764a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling    private Notification.Builder mNotificationBuilder;
774a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling    private NotificationManager mNotificationManager;
7858acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling
7958acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling    /** Sending this broadcast intent will cause the processing to pause. */
8058acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling    public static final String ACTION_PAUSE_PROCESSING_SERVICE =
8158acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling            "com.android.camera.processing.PAUSE";
8258acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling    /**
8358acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling     * Sending this broadcast intent will cause the processing to resume after
8458acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling     * it has been paused.
8558acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling     */
8658acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling    public static final String ACTION_RESUME_PROCESSING_SERVICE =
8758acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling            "com.android.camera.processing.RESUME";
8858acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling
89dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    private WakeLock mWakeLock;
9058acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling    private final ServiceController mServiceController = new ServiceController();
91dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
92dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    /** Manages the capture session. */
93dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    private CaptureSessionManager mSessionManager;
94dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
95dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    private ProcessingServiceManager mProcessingServiceManager;
96dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    private Thread mProcessingThread;
9758acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling    private volatile boolean mPaused = false;
9858acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling    private ProcessingTask mCurrentTask;
9958acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling    private final Lock mSuspendStatusLock = new ReentrantLock();
100dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
101dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    @Override
102dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    public void onCreate() {
103dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        mProcessingServiceManager = ProcessingServiceManager.getInstance();
104dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        mSessionManager = getServices().getCaptureSessionManager();
105dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
106dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        // Keep CPU awake while allowing screen and keyboard to switch off.
107dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        PowerManager powerManager = (PowerManager) getSystemService(
108dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling                Context.POWER_SERVICE);
1095596b4c902dcb685928b43678f428746ca5ffd08Angus Kong        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG.toString());
110dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        mWakeLock.acquire();
11158acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling
11258acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling        IntentFilter intentFilter = new IntentFilter();
11358acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling        intentFilter.addAction(ACTION_PAUSE_PROCESSING_SERVICE);
11458acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling        intentFilter.addAction(ACTION_RESUME_PROCESSING_SERVICE);
11558acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling        LocalBroadcastManager.getInstance(this).registerReceiver(mServiceController, intentFilter);
1164a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling        mNotificationBuilder = createInProgressNotificationBuilder();
1174a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling        mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
118dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    }
119dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
120dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    @Override
121dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    public void onDestroy() {
122dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        Log.d(TAG, "Shutting down");
123dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        // TODO: Cancel session in progress...
124dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
125dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        // Unlock the power manager, i.e. let power management kick in if
126dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        // needed.
127dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        if (mWakeLock.isHeld()) {
128dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling            mWakeLock.release();
129dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        }
13058acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling        LocalBroadcastManager.getInstance(this).unregisterReceiver(mServiceController);
1314a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling        stopForeground(true);
132dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    }
133dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
134dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    @Override
135dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    public int onStartCommand(Intent intent, int flags, int startId) {
1364a5b9e0b9f4eab654f30588941749a25aeba97feSascha Haeberling        Log.d(TAG, "Starting in foreground.");
1374a5b9e0b9f4eab654f30588941749a25aeba97feSascha Haeberling
1384a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling        // We need to start this service in foreground so that it's not getting
1394a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling        // killed easily when memory pressure is building up.
1404a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling        startForeground(CAMERA_NOTIFICATION_ID, mNotificationBuilder.build());
14158acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling
142dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        asyncProcessAllTasksAndShutdown();
143dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
144dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        // We want this service to continue running until it is explicitly
145dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        // stopped, so return sticky.
146dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        return START_STICKY;
147dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    }
148dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
149dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    @Override
150dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    public IBinder onBind(Intent intent) {
151dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        // We don't provide binding, so return null.
152dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        return null;
153dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    }
154dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
15558acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling    private void pause() {
1564a5b9e0b9f4eab654f30588941749a25aeba97feSascha Haeberling        Log.d(TAG, "Pausing");
15758acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling        try {
15858acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling            mSuspendStatusLock.lock();
15958acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling            mPaused = true;
16058acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling            if (mCurrentTask != null) {
16158acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling                mCurrentTask.suspend();
16258acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling            }
16358acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling        } finally {
16458acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling            mSuspendStatusLock.unlock();
16558acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling        }
16658acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling    }
16758acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling
16858acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling    private void resume() {
1694a5b9e0b9f4eab654f30588941749a25aeba97feSascha Haeberling        Log.d(TAG, "Resuming");
17058acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling        try {
17158acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling            mSuspendStatusLock.lock();
17258acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling            mPaused = false;
17358acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling            if (mCurrentTask != null) {
17458acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling                mCurrentTask.resume();
17558acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling            }
17658acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling        } finally {
17758acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling            mSuspendStatusLock.unlock();
17858acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling        }
17958acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling    }
18058acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling
181dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    /**
182dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling     * Starts a thread to process all tasks. When no more tasks are in the
183dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling     * queue, it exits the thread and shuts down the service.
184dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling     */
185dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    private void asyncProcessAllTasksAndShutdown() {
186dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        if (mProcessingThread != null) {
187dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling            return;
188dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        }
18958acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling        mProcessingThread = new Thread("CameraProcessingThread") {
190dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling            @Override
191dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling            public void run() {
192dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling                // Set the thread priority
193dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling                android.os.Process.setThreadPriority(THREAD_PRIORITY);
194dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
195dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling                ProcessingTask task;
196dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling                while ((task = mProcessingServiceManager.popNextSession()) != null) {
19758acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling                    mCurrentTask = task;
19858acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling                    try {
19958acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling                        mSuspendStatusLock.lock();
20058acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling                        if (mPaused) {
20158acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling                            mCurrentTask.suspend();
20258acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling                        }
20358acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling                    } finally {
20458acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling                        mSuspendStatusLock.unlock();
20558acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling                    }
206dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling                    processAndNotify(task);
207dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling                }
208dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling                stopSelf();
209dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling            }
210dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        };
211dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        mProcessingThread.start();
212dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    }
213dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
214dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    /**
215dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling     * Processes a {@code ProcessingTask} and updates the notification bar.
216dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling     */
217dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    void processAndNotify(ProcessingTask task) {
218dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        if (task == null) {
219dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling            Log.e(TAG, "Reference to ProcessingTask is null");
220dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling            return;
221dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        }
2222a978f2b7b07ce4e5f6163910912e79fed38aff8Seth Raphael        CaptureSession session = task.getSession();
2232a978f2b7b07ce4e5f6163910912e79fed38aff8Seth Raphael        if (session == null) {
22457bcd92f39d85427102f30be2c749c4a7bd4be96Sascha Haeberling            // TODO: Timestamp is not required right now, refactor this to make it clearer.
22557bcd92f39d85427102f30be2c749c4a7bd4be96Sascha Haeberling            session = mSessionManager.createNewSession(task.getName(), 0, task.getLocation());
2262a978f2b7b07ce4e5f6163910912e79fed38aff8Seth Raphael        }
2274a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling        resetNotification();
2284a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling
2294a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling        // Adding the listener also causes it to get called for the session's
2304a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling        // current status message and percent completed.
2314a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling        session.addProgressListener(this);
2324a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling
23358acf0df808ea67e8ad9ab6ffccff4548566580aSascha Haeberling        System.gc();
2344a5b9e0b9f4eab654f30588941749a25aeba97feSascha Haeberling        Log.d(TAG, "Processing start");
2352a978f2b7b07ce4e5f6163910912e79fed38aff8Seth Raphael        task.process(this, getServices(), session);
2364a5b9e0b9f4eab654f30588941749a25aeba97feSascha Haeberling        Log.d(TAG, "Processing done");
237dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    }
238dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling
2394a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling    private void resetNotification() {
2404a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling        mNotificationBuilder.setContentText("…").setProgress(100, 0, false);
2414a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling        postNotification();
2424a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling    }
2434a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling
244dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    /**
245dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling     * Returns the common camera services.
246dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling     */
247dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    private CameraServices getServices() {
248dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling        return (CameraApp) this.getApplication();
249dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling    }
2504a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling
2514a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling    private void postNotification() {
2524a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling        mNotificationManager.notify(CAMERA_NOTIFICATION_ID, mNotificationBuilder.build());
2534a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling    }
2544a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling
2554a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling    /**
2564a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling     * Creates a notification to indicate that a computation is in progress.
2574a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling     */
2584a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling    private Notification.Builder createInProgressNotificationBuilder() {
2594a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling        return new Notification.Builder(this)
2604a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling                .setSmallIcon(R.drawable.ic_notification)
2614a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling                .setWhen(System.currentTimeMillis())
2624a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling                .setOngoing(true)
2634a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling                .setContentTitle(this.getText(R.string.app_name));
2644a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling    }
2654a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling
2664a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling    @Override
2674a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling    public void onProgressChanged(int progress) {
2684a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling        mNotificationBuilder.setProgress(100, progress, false);
2694a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling        postNotification();
2704a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling    }
2714a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling
2724a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling    @Override
2734a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling    public void onStatusMessageChanged(CharSequence message) {
2744a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling        mNotificationBuilder.setContentText(message);
2754a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling        postNotification();
2764a400d7f09ea0655d4730e904892e3a9d96993ebSascha Haeberling    }
277dd2d9e610aa9dc71fe0938f38de43bb0e26f3bb2Sascha Haeberling}
278