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.Log; 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 Log.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 Log.v("There is no current alarm to stop"); 116 return; 117 } 118 119 Log.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 Log.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 Log.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 Log.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 Log.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 Log.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