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