Home | History | Annotate | Download | only in alarms
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.deskclock.alarms;
     17 
     18 import android.app.Service;
     19 import android.content.ContentResolver;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.os.IBinder;
     23 import android.telephony.PhoneStateListener;
     24 import android.telephony.TelephonyManager;
     25 
     26 import com.android.deskclock.AlarmAlertWakeLock;
     27 import com.android.deskclock.LogUtils;
     28 import com.android.deskclock.provider.AlarmInstance;
     29 
     30 /**
     31  * This service is in charge of starting/stoping the alarm. It will bring up and manage the
     32  * {@link AlarmActivity} as well as {@link AlarmKlaxon}.
     33  */
     34 public class AlarmService extends Service {
     35     // A public action send by AlarmService when the alarm has started.
     36     public static final String ALARM_ALERT_ACTION = "com.android.deskclock.ALARM_ALERT";
     37 
     38     // A public action sent by AlarmService when the alarm has stopped for any reason.
     39     public static final String ALARM_DONE_ACTION = "com.android.deskclock.ALARM_DONE";
     40 
     41     // Private action used to start an alarm with this service.
     42     public static final String START_ALARM_ACTION = "START_ALARM";
     43 
     44     // Private action used to stop an alarm with this service.
     45     public static final String STOP_ALARM_ACTION = "STOP_ALARM";
     46 
     47     /**
     48      * Utility method to help start alarm properly. If alarm is already firing, it
     49      * will mark it as missed and start the new one.
     50      *
     51      * @param context application context
     52      * @param instance to trigger alarm
     53      */
     54     public static void startAlarm(Context context, AlarmInstance instance) {
     55         Intent intent = AlarmInstance.createIntent(context, AlarmService.class, instance.mId);
     56         intent.setAction(START_ALARM_ACTION);
     57 
     58         // Maintain a cpu wake lock until the service can get it
     59         AlarmAlertWakeLock.acquireCpuWakeLock(context);
     60         context.startService(intent);
     61     }
     62 
     63     /**
     64      * Utility method to help stop an alarm properly. Nothing will happen, if alarm is not firing
     65      * or using a different instance.
     66      *
     67      * @param context application context
     68      * @param instance you are trying to stop
     69      */
     70     public static void stopAlarm(Context context, AlarmInstance instance) {
     71         Intent intent = AlarmInstance.createIntent(context, AlarmService.class, instance.mId);
     72         intent.setAction(STOP_ALARM_ACTION);
     73 
     74         // We don't need a wake lock here, since we are trying to kill an alarm
     75         context.startService(intent);
     76     }
     77 
     78     private TelephonyManager mTelephonyManager;
     79     private int mInitialCallState;
     80     private AlarmInstance mCurrentAlarm = null;
     81 
     82     private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
     83         @Override
     84         public void onCallStateChanged(int state, String ignored) {
     85             // The user might already be in a call when the alarm fires. When
     86             // we register onCallStateChanged, we get the initial in-call state
     87             // which kills the alarm. Check against the initial call state so
     88             // we don't kill the alarm during a call.
     89             if (state != TelephonyManager.CALL_STATE_IDLE && state != mInitialCallState) {
     90                 sendBroadcast(AlarmStateManager.createStateChangeIntent(AlarmService.this,
     91                         "AlarmService", mCurrentAlarm, AlarmInstance.MISSED_STATE));
     92             }
     93         }
     94     };
     95 
     96     private void startAlarm(AlarmInstance instance) {
     97         LogUtils.v("AlarmService.start with instance: " + instance.mId);
     98         if (mCurrentAlarm != null) {
     99             AlarmStateManager.setMissedState(this, mCurrentAlarm);
    100             stopCurrentAlarm();
    101         }
    102 
    103         AlarmAlertWakeLock.acquireCpuWakeLock(this);
    104         mCurrentAlarm = instance;
    105         AlarmNotifications.showAlarmNotification(this, mCurrentAlarm);
    106         mInitialCallState = mTelephonyManager.getCallState();
    107         mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
    108         boolean inCall = mInitialCallState != TelephonyManager.CALL_STATE_IDLE;
    109         AlarmKlaxon.start(this, mCurrentAlarm, inCall);
    110         sendBroadcast(new Intent(ALARM_ALERT_ACTION));
    111     }
    112 
    113     private void stopCurrentAlarm() {
    114         if (mCurrentAlarm == null) {
    115             LogUtils.v("There is no current alarm to stop");
    116             return;
    117         }
    118 
    119         LogUtils.v("AlarmService.stop with instance: " + mCurrentAlarm.mId);
    120         AlarmKlaxon.stop(this);
    121         mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
    122         sendBroadcast(new Intent(ALARM_DONE_ACTION));
    123         mCurrentAlarm = null;
    124         AlarmAlertWakeLock.releaseCpuLock();
    125     }
    126 
    127     @Override
    128     public void onCreate() {
    129         super.onCreate();
    130         mTelephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
    131     }
    132 
    133     @Override
    134     public int onStartCommand(Intent intent, int flags, int startId) {
    135         LogUtils.v("AlarmService.onStartCommand() with intent: " + intent.toString());
    136 
    137         long instanceId = AlarmInstance.getId(intent.getData());
    138         if (START_ALARM_ACTION.equals(intent.getAction())) {
    139             ContentResolver cr = this.getContentResolver();
    140             AlarmInstance instance = AlarmInstance.getInstance(cr, instanceId);
    141             if (instance == null) {
    142                 LogUtils.e("No instance found to start alarm: " + instanceId);
    143                 if (mCurrentAlarm != null) {
    144                     // Only release lock if we are not firing alarm
    145                     AlarmAlertWakeLock.releaseCpuLock();
    146                 }
    147                 return Service.START_NOT_STICKY;
    148             } else if (mCurrentAlarm != null && mCurrentAlarm.mId == instanceId) {
    149                 LogUtils.e("Alarm already started for instance: " + instanceId);
    150                 return Service.START_NOT_STICKY;
    151             }
    152             startAlarm(instance);
    153         } else if(STOP_ALARM_ACTION.equals(intent.getAction())) {
    154             if (mCurrentAlarm != null && mCurrentAlarm.mId != instanceId) {
    155                 LogUtils.e("Can't stop alarm for instance: " + instanceId +
    156                         " because current alarm is: " + mCurrentAlarm.mId);
    157                 return Service.START_NOT_STICKY;
    158             }
    159             stopSelf();
    160         }
    161 
    162         return Service.START_NOT_STICKY;
    163     }
    164 
    165     @Override
    166     public void onDestroy() {
    167         LogUtils.v("AlarmService.onDestroy() called");
    168         super.onDestroy();
    169         stopCurrentAlarm();
    170     }
    171 
    172     @Override
    173     public IBinder onBind(Intent intent) {
    174         return null;
    175     }
    176 }
    177