11c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski/*
21c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski * Copyright (C) 2013 The Android Open Source Project
31c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski *
41c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski * Licensed under the Apache License, Version 2.0 (the "License");
51c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski * you may not use this file except in compliance with the License.
61c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski * You may obtain a copy of the License at
71c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski *
81c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski *      http://www.apache.org/licenses/LICENSE-2.0
91c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski *
101c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski * Unless required by applicable law or agreed to in writing, software
111c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski * distributed under the License is distributed on an "AS IS" BASIS,
121c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski * See the License for the specific language governing permissions and
141c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski * limitations under the License.
151c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski */
161c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowskipackage com.android.deskclock.alarms;
171c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
181c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowskiimport android.app.Service;
191c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowskiimport android.content.ContentResolver;
201c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowskiimport android.content.Context;
211c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowskiimport android.content.Intent;
221c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowskiimport android.os.IBinder;
231c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowskiimport android.telephony.PhoneStateListener;
241c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowskiimport android.telephony.TelephonyManager;
251c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
261c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowskiimport com.android.deskclock.AlarmAlertWakeLock;
271c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowskiimport com.android.deskclock.Log;
281c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowskiimport com.android.deskclock.provider.AlarmInstance;
291c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
301c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski/**
311c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski * This service is in charge of starting/stoping the alarm. It will bring up and manage the
321c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski * {@link AlarmActivity} as well as {@link AlarmKlaxon}.
331c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski */
341c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowskipublic class AlarmService extends Service {
351c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    // A public action send by AlarmService when the alarm has started.
361c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    public static final String ALARM_ALERT_ACTION = "com.android.deskclock.ALARM_ALERT";
371c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
381c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    // A public action sent by AlarmService when the alarm has stopped for any reason.
391c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    public static final String ALARM_DONE_ACTION = "com.android.deskclock.ALARM_DONE";
401c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
411c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    // Private action used to start an alarm with this service.
421c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    public static final String START_ALARM_ACTION = "START_ALARM";
431c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
441c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    // Private action used to stop an alarm with this service.
451c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    public static final String STOP_ALARM_ACTION = "STOP_ALARM";
461c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
471c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    /**
481c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski     * Utility method to help start alarm properly. If alarm is already firing, it
491c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski     * will mark it as missed and start the new one.
501c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski     *
511c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski     * @param context application context
521c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski     * @param instance to trigger alarm
531c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski     */
541c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    public static void startAlarm(Context context, AlarmInstance instance) {
551c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        Intent intent = AlarmInstance.createIntent(context, AlarmService.class, instance.mId);
561c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        intent.setAction(START_ALARM_ACTION);
571c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
581c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        // Maintain a cpu wake lock until the service can get it
591c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        AlarmAlertWakeLock.acquireCpuWakeLock(context);
601c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        context.startService(intent);
611c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    }
621c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
631c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    /**
641c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski     * Utility method to help stop an alarm properly. Nothing will happen, if alarm is not firing
651c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski     * or using a different instance.
661c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski     *
671c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski     * @param context application context
681c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski     * @param instance you are trying to stop
691c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski     */
701c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    public static void stopAlarm(Context context, AlarmInstance instance) {
711c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        Intent intent = AlarmInstance.createIntent(context, AlarmService.class, instance.mId);
721c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        intent.setAction(STOP_ALARM_ACTION);
731c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
741c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        // We don't need a wake lock here, since we are trying to kill an alarm
751c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        context.startService(intent);
761c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    }
771c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
781c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    private TelephonyManager mTelephonyManager;
791c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    private int mInitialCallState;
801c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    private AlarmInstance mCurrentAlarm = null;
811c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
821c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
831c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        @Override
841c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        public void onCallStateChanged(int state, String ignored) {
851c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            // The user might already be in a call when the alarm fires. When
861c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            // we register onCallStateChanged, we get the initial in-call state
871c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            // which kills the alarm. Check against the initial call state so
881c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            // we don't kill the alarm during a call.
891c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            if (state != TelephonyManager.CALL_STATE_IDLE && state != mInitialCallState) {
901c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                sendBroadcast(AlarmStateManager.createStateChangeIntent(AlarmService.this,
911c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                        "AlarmService", mCurrentAlarm, AlarmInstance.MISSED_STATE));
921c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            }
931c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        }
941c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    };
951c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
961c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    private void startAlarm(AlarmInstance instance) {
971c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        Log.v("AlarmService.start with instance: " + instance.mId);
981c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        if (mCurrentAlarm != null) {
991c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            AlarmStateManager.setMissedState(this, mCurrentAlarm);
1001c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            stopCurrentAlarm();
1011c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        }
1021c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
1031c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        AlarmAlertWakeLock.acquireCpuWakeLock(this);
1041c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        mCurrentAlarm = instance;
1051c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        AlarmNotifications.showAlarmNotification(this, mCurrentAlarm);
1061c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        mInitialCallState = mTelephonyManager.getCallState();
1071c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
1081c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        boolean inCall = mInitialCallState != TelephonyManager.CALL_STATE_IDLE;
1091c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        AlarmKlaxon.start(this, mCurrentAlarm, inCall);
1101c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        sendBroadcast(new Intent(ALARM_ALERT_ACTION));
1111c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    }
1121c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
1131c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    private void stopCurrentAlarm() {
1141c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        if (mCurrentAlarm == null) {
1151c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            Log.v("There is no current alarm to stop");
1161c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            return;
1171c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        }
1181c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
1191c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        Log.v("AlarmService.stop with instance: " + mCurrentAlarm.mId);
1201c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        AlarmKlaxon.stop(this);
1211c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
1221c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        sendBroadcast(new Intent(ALARM_DONE_ACTION));
1231c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        mCurrentAlarm = null;
1241c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        AlarmAlertWakeLock.releaseCpuLock();
1251c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    }
1261c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
1271c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    @Override
1281c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    public void onCreate() {
1291c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        super.onCreate();
1301c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        mTelephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
1311c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    }
1321c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
1331c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    @Override
1341c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    public int onStartCommand(Intent intent, int flags, int startId) {
1351c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        Log.v("AlarmService.onStartCommand() with intent: " + intent.toString());
1361c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
1371c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        long instanceId = AlarmInstance.getId(intent.getData());
1381c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        if (START_ALARM_ACTION.equals(intent.getAction())) {
1391c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            ContentResolver cr = this.getContentResolver();
1401c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            AlarmInstance instance = AlarmInstance.getInstance(cr, instanceId);
1411c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            if (instance == null) {
1421c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                Log.e("No instance found to start alarm: " + instanceId);
1431c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                if (mCurrentAlarm != null) {
1441c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                    // Only release lock if we are not firing alarm
1451c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                    AlarmAlertWakeLock.releaseCpuLock();
1461c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                }
1471c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                return Service.START_NOT_STICKY;
1481c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            } else if (mCurrentAlarm != null && mCurrentAlarm.mId == instanceId) {
1491c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                Log.e("Alarm already started for instance: " + instanceId);
1501c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                return Service.START_NOT_STICKY;
1511c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            }
1521c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            startAlarm(instance);
1531c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        } else if(STOP_ALARM_ACTION.equals(intent.getAction())) {
1541c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            if (mCurrentAlarm != null && mCurrentAlarm.mId != instanceId) {
1551c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                Log.e("Can't stop alarm for instance: " + instanceId +
1561c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                    " because current alarm is: " + mCurrentAlarm.mId);
1571c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski                return Service.START_NOT_STICKY;
1581c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski            }
15991441304feead2d27a2dbdf108526e9c49bb91f9Paul Sliwowski            stopSelf();
1601c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        }
1611c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
1621c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        return Service.START_NOT_STICKY;
1631c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    }
1641c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
1651c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    @Override
1661c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    public void onDestroy() {
1671c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        Log.v("AlarmService.onDestroy() called");
1681c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        super.onDestroy();
1691c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        stopCurrentAlarm();
1701c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    }
1711c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski
1721c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    @Override
1731c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    public IBinder onBind(Intent intent) {
1741c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski        return null;
1751c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski    }
1761c7788b33dd4516dae81e6bcab043addc45fc1a1Paul Sliwowski}
177