12019a976f07ff418dde2dfc7cc74667ef66d7764sewardj/*
22019a976f07ff418dde2dfc7cc74667ef66d7764sewardj * Copyright (C) 2014 The Android Open Source Project
32019a976f07ff418dde2dfc7cc74667ef66d7764sewardj *
42019a976f07ff418dde2dfc7cc74667ef66d7764sewardj * Licensed under the Apache License, Version 2.0 (the "License");
52019a976f07ff418dde2dfc7cc74667ef66d7764sewardj * you may not use this file except in compliance with the License.
62019a976f07ff418dde2dfc7cc74667ef66d7764sewardj * You may obtain a copy of the License at
72019a976f07ff418dde2dfc7cc74667ef66d7764sewardj *
82019a976f07ff418dde2dfc7cc74667ef66d7764sewardj *      http://www.apache.org/licenses/LICENSE-2.0
92019a976f07ff418dde2dfc7cc74667ef66d7764sewardj *
102019a976f07ff418dde2dfc7cc74667ef66d7764sewardj * Unless required by applicable law or agreed to in writing, software
1189ae8477745fd2a15453557d729a50e627325ee2sewardj * distributed under the License is distributed on an "AS IS" BASIS,
122019a976f07ff418dde2dfc7cc74667ef66d7764sewardj * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132019a976f07ff418dde2dfc7cc74667ef66d7764sewardj * See the License for the specific language governing permissions and
142019a976f07ff418dde2dfc7cc74667ef66d7764sewardj * limitations under the License
152019a976f07ff418dde2dfc7cc74667ef66d7764sewardj */
162019a976f07ff418dde2dfc7cc74667ef66d7764sewardj
172019a976f07ff418dde2dfc7cc74667ef66d7764sewardjpackage com.android.server.telecom.testapps;
182019a976f07ff418dde2dfc7cc74667ef66d7764sewardj
192019a976f07ff418dde2dfc7cc74667ef66d7764sewardjimport android.os.ConditionVariable;
202019a976f07ff418dde2dfc7cc74667ef66d7764sewardjimport android.os.Handler;
212019a976f07ff418dde2dfc7cc74667ef66d7764sewardjimport android.os.Looper;
222019a976f07ff418dde2dfc7cc74667ef66d7764sewardjimport android.util.Log;
232019a976f07ff418dde2dfc7cc74667ef66d7764sewardj
242019a976f07ff418dde2dfc7cc74667ef66d7764sewardjimport java.lang.AutoCloseable;
252019a976f07ff418dde2dfc7cc74667ef66d7764sewardjimport java.lang.Exception;
262019a976f07ff418dde2dfc7cc74667ef66d7764sewardjimport java.lang.Override;
272019a976f07ff418dde2dfc7cc74667ef66d7764sewardjimport java.lang.String;
282019a976f07ff418dde2dfc7cc74667ef66d7764sewardjimport java.lang.Thread;
292019a976f07ff418dde2dfc7cc74667ef66d7764sewardjimport java.lang.Throwable;
302019a976f07ff418dde2dfc7cc74667ef66d7764sewardjimport java.util.concurrent.TimeoutException;
312019a976f07ff418dde2dfc7cc74667ef66d7764sewardj
322019a976f07ff418dde2dfc7cc74667ef66d7764sewardj/**
332019a976f07ff418dde2dfc7cc74667ef66d7764sewardj * Camera thread class used for handling camera callbacks.
3433b024301d2311965cc68dc4cc900f3d0fdd8085florian */
352019a976f07ff418dde2dfc7cc74667ef66d7764sewardjpublic class CameraThread implements AutoCloseable {
362019a976f07ff418dde2dfc7cc74667ef66d7764sewardj    private static final String TAG = "CameraThread";
372019a976f07ff418dde2dfc7cc74667ef66d7764sewardj    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
38933065d4f3548466da4666c5cfda7e5eaff93759florian
392019a976f07ff418dde2dfc7cc74667ef66d7764sewardj    // Timeout for initializing looper and opening camera in Milliseconds.
402019a976f07ff418dde2dfc7cc74667ef66d7764sewardj    private static final long WAIT_FOR_COMMAND_TO_COMPLETE = 5000;
416c46befd9eb90c1b6e739926c1fa335cba75bf46philippe    private Looper mLooper = null;
422019a976f07ff418dde2dfc7cc74667ef66d7764sewardj    private Handler mHandler = null;
432019a976f07ff418dde2dfc7cc74667ef66d7764sewardj
44b0b6710aa770763fd1fd5005075de8a8d5a269fbflorian    /**
452019a976f07ff418dde2dfc7cc74667ef66d7764sewardj     * Create and start a looper thread, return the Handler
462019a976f07ff418dde2dfc7cc74667ef66d7764sewardj     */
472019a976f07ff418dde2dfc7cc74667ef66d7764sewardj    public synchronized Handler start() throws Exception {
482019a976f07ff418dde2dfc7cc74667ef66d7764sewardj        final ConditionVariable startDone = new ConditionVariable();
492019a976f07ff418dde2dfc7cc74667ef66d7764sewardj        if (mHandler != null) {
502019a976f07ff418dde2dfc7cc74667ef66d7764sewardj            Log.w(TAG, "Looper thread already started");
512019a976f07ff418dde2dfc7cc74667ef66d7764sewardj            return mHandler;
522019a976f07ff418dde2dfc7cc74667ef66d7764sewardj        }
532019a976f07ff418dde2dfc7cc74667ef66d7764sewardj
542019a976f07ff418dde2dfc7cc74667ef66d7764sewardj        new Thread() {
552019a976f07ff418dde2dfc7cc74667ef66d7764sewardj            @Override
562019a976f07ff418dde2dfc7cc74667ef66d7764sewardj            public void run() {
572019a976f07ff418dde2dfc7cc74667ef66d7764sewardj                if (VERBOSE) Log.v(TAG, "start loopRun");
582019a976f07ff418dde2dfc7cc74667ef66d7764sewardj                Looper.prepare();
592019a976f07ff418dde2dfc7cc74667ef66d7764sewardj                // Save the looper so that we can terminate this thread
602019a976f07ff418dde2dfc7cc74667ef66d7764sewardj                // after we are done with it.
612019a976f07ff418dde2dfc7cc74667ef66d7764sewardj                mLooper = Looper.myLooper();
622019a976f07ff418dde2dfc7cc74667ef66d7764sewardj                mHandler = new Handler();
632019a976f07ff418dde2dfc7cc74667ef66d7764sewardj                startDone.open();
642019a976f07ff418dde2dfc7cc74667ef66d7764sewardj                Looper.loop();
652019a976f07ff418dde2dfc7cc74667ef66d7764sewardj                if (VERBOSE) Log.v(TAG, "createLooperThread: finished");
662019a976f07ff418dde2dfc7cc74667ef66d7764sewardj            }
672019a976f07ff418dde2dfc7cc74667ef66d7764sewardj        }.start();
682019a976f07ff418dde2dfc7cc74667ef66d7764sewardj
692019a976f07ff418dde2dfc7cc74667ef66d7764sewardj        if (VERBOSE) Log.v(TAG, "start waiting for looper");
702019a976f07ff418dde2dfc7cc74667ef66d7764sewardj        if (!startDone.block(WAIT_FOR_COMMAND_TO_COMPLETE)) {
712019a976f07ff418dde2dfc7cc74667ef66d7764sewardj            throw new TimeoutException("createLooperThread: start timeout");
722019a976f07ff418dde2dfc7cc74667ef66d7764sewardj        }
732019a976f07ff418dde2dfc7cc74667ef66d7764sewardj        return mHandler;
742019a976f07ff418dde2dfc7cc74667ef66d7764sewardj    }
752019a976f07ff418dde2dfc7cc74667ef66d7764sewardj
762019a976f07ff418dde2dfc7cc74667ef66d7764sewardj    /**
772019a976f07ff418dde2dfc7cc74667ef66d7764sewardj     * Terminate the looper thread
782019a976f07ff418dde2dfc7cc74667ef66d7764sewardj     */
792019a976f07ff418dde2dfc7cc74667ef66d7764sewardj    public synchronized void close() throws Exception {
802019a976f07ff418dde2dfc7cc74667ef66d7764sewardj        if (mLooper == null || mHandler == null) {
812019a976f07ff418dde2dfc7cc74667ef66d7764sewardj            Log.w(TAG, "Looper thread doesn't start yet");
822019a976f07ff418dde2dfc7cc74667ef66d7764sewardj            return;
832019a976f07ff418dde2dfc7cc74667ef66d7764sewardj        }
842019a976f07ff418dde2dfc7cc74667ef66d7764sewardj
852019a976f07ff418dde2dfc7cc74667ef66d7764sewardj        if (VERBOSE) Log.v(TAG, "Terminate looper thread");
862019a976f07ff418dde2dfc7cc74667ef66d7764sewardj        mLooper.quit();
872019a976f07ff418dde2dfc7cc74667ef66d7764sewardj        mLooper.getThread().join();
882019a976f07ff418dde2dfc7cc74667ef66d7764sewardj        mLooper = null;
892019a976f07ff418dde2dfc7cc74667ef66d7764sewardj        mHandler = null;
902019a976f07ff418dde2dfc7cc74667ef66d7764sewardj    }
912019a976f07ff418dde2dfc7cc74667ef66d7764sewardj
922019a976f07ff418dde2dfc7cc74667ef66d7764sewardj    @Override
932019a976f07ff418dde2dfc7cc74667ef66d7764sewardj    protected void finalize() throws Throwable {
942019a976f07ff418dde2dfc7cc74667ef66d7764sewardj        try {
952019a976f07ff418dde2dfc7cc74667ef66d7764sewardj            close();
962019a976f07ff418dde2dfc7cc74667ef66d7764sewardj        } finally {
972019a976f07ff418dde2dfc7cc74667ef66d7764sewardj            super.finalize();
982019a976f07ff418dde2dfc7cc74667ef66d7764sewardj        }
992019a976f07ff418dde2dfc7cc74667ef66d7764sewardj    }
1002019a976f07ff418dde2dfc7cc74667ef66d7764sewardj}
1012019a976f07ff418dde2dfc7cc74667ef66d7764sewardj