| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.android.deskclock.alarms; |
| |
| import android.app.Service; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.os.IBinder; |
| import android.telephony.PhoneStateListener; |
| import android.telephony.TelephonyManager; |
| |
| import com.android.deskclock.AlarmAlertWakeLock; |
| import com.android.deskclock.LogUtils; |
| import com.android.deskclock.provider.AlarmInstance; |
| |
| /** |
| * This service is in charge of starting/stoping the alarm. It will bring up and manage the |
| * {@link AlarmActivity} as well as {@link AlarmKlaxon}. |
| */ |
| public class AlarmService extends Service { |
| // A public action send by AlarmService when the alarm has started. |
| public static final String ALARM_ALERT_ACTION = "com.android.deskclock.ALARM_ALERT"; |
| |
| // A public action sent by AlarmService when the alarm has stopped for any reason. |
| public static final String ALARM_DONE_ACTION = "com.android.deskclock.ALARM_DONE"; |
| |
| // Private action used to start an alarm with this service. |
| public static final String START_ALARM_ACTION = "START_ALARM"; |
| |
| // Private action used to stop an alarm with this service. |
| public static final String STOP_ALARM_ACTION = "STOP_ALARM"; |
| |
| /** |
| * Utility method to help start alarm properly. If alarm is already firing, it |
| * will mark it as missed and start the new one. |
| * |
| * @param context application context |
| * @param instance to trigger alarm |
| */ |
| public static void startAlarm(Context context, AlarmInstance instance) { |
| Intent intent = AlarmInstance.createIntent(context, AlarmService.class, instance.mId); |
| intent.setAction(START_ALARM_ACTION); |
| |
| // Maintain a cpu wake lock until the service can get it |
| AlarmAlertWakeLock.acquireCpuWakeLock(context); |
| context.startService(intent); |
| } |
| |
| /** |
| * Utility method to help stop an alarm properly. Nothing will happen, if alarm is not firing |
| * or using a different instance. |
| * |
| * @param context application context |
| * @param instance you are trying to stop |
| */ |
| public static void stopAlarm(Context context, AlarmInstance instance) { |
| Intent intent = AlarmInstance.createIntent(context, AlarmService.class, instance.mId); |
| intent.setAction(STOP_ALARM_ACTION); |
| |
| // We don't need a wake lock here, since we are trying to kill an alarm |
| context.startService(intent); |
| } |
| |
| private TelephonyManager mTelephonyManager; |
| private int mInitialCallState; |
| private AlarmInstance mCurrentAlarm = null; |
| |
| private PhoneStateListener mPhoneStateListener = new PhoneStateListener() { |
| @Override |
| public void onCallStateChanged(int state, String ignored) { |
| // The user might already be in a call when the alarm fires. When |
| // we register onCallStateChanged, we get the initial in-call state |
| // which kills the alarm. Check against the initial call state so |
| // we don't kill the alarm during a call. |
| if (state != TelephonyManager.CALL_STATE_IDLE && state != mInitialCallState) { |
| sendBroadcast(AlarmStateManager.createStateChangeIntent(AlarmService.this, |
| "AlarmService", mCurrentAlarm, AlarmInstance.MISSED_STATE)); |
| } |
| } |
| }; |
| |
| private void startAlarm(AlarmInstance instance) { |
| LogUtils.v("AlarmService.start with instance: " + instance.mId); |
| if (mCurrentAlarm != null) { |
| AlarmStateManager.setMissedState(this, mCurrentAlarm); |
| stopCurrentAlarm(); |
| } |
| |
| AlarmAlertWakeLock.acquireCpuWakeLock(this); |
| mCurrentAlarm = instance; |
| AlarmNotifications.showAlarmNotification(this, mCurrentAlarm); |
| mInitialCallState = mTelephonyManager.getCallState(); |
| mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); |
| boolean inCall = mInitialCallState != TelephonyManager.CALL_STATE_IDLE; |
| AlarmKlaxon.start(this, mCurrentAlarm, inCall); |
| sendBroadcast(new Intent(ALARM_ALERT_ACTION)); |
| } |
| |
| private void stopCurrentAlarm() { |
| if (mCurrentAlarm == null) { |
| LogUtils.v("There is no current alarm to stop"); |
| return; |
| } |
| |
| LogUtils.v("AlarmService.stop with instance: " + mCurrentAlarm.mId); |
| AlarmKlaxon.stop(this); |
| mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); |
| sendBroadcast(new Intent(ALARM_DONE_ACTION)); |
| mCurrentAlarm = null; |
| AlarmAlertWakeLock.releaseCpuLock(); |
| } |
| |
| @Override |
| public void onCreate() { |
| super.onCreate(); |
| mTelephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); |
| } |
| |
| @Override |
| public int onStartCommand(Intent intent, int flags, int startId) { |
| LogUtils.v("AlarmService.onStartCommand() with intent: " + intent.toString()); |
| |
| long instanceId = AlarmInstance.getId(intent.getData()); |
| if (START_ALARM_ACTION.equals(intent.getAction())) { |
| ContentResolver cr = this.getContentResolver(); |
| AlarmInstance instance = AlarmInstance.getInstance(cr, instanceId); |
| if (instance == null) { |
| LogUtils.e("No instance found to start alarm: " + instanceId); |
| if (mCurrentAlarm != null) { |
| // Only release lock if we are not firing alarm |
| AlarmAlertWakeLock.releaseCpuLock(); |
| } |
| return Service.START_NOT_STICKY; |
| } else if (mCurrentAlarm != null && mCurrentAlarm.mId == instanceId) { |
| LogUtils.e("Alarm already started for instance: " + instanceId); |
| return Service.START_NOT_STICKY; |
| } |
| startAlarm(instance); |
| } else if(STOP_ALARM_ACTION.equals(intent.getAction())) { |
| if (mCurrentAlarm != null && mCurrentAlarm.mId != instanceId) { |
| LogUtils.e("Can't stop alarm for instance: " + instanceId + |
| " because current alarm is: " + mCurrentAlarm.mId); |
| return Service.START_NOT_STICKY; |
| } |
| stopSelf(); |
| } |
| |
| return Service.START_NOT_STICKY; |
| } |
| |
| @Override |
| public void onDestroy() { |
| LogUtils.v("AlarmService.onDestroy() called"); |
| super.onDestroy(); |
| stopCurrentAlarm(); |
| } |
| |
| @Override |
| public IBinder onBind(Intent intent) { |
| return null; |
| } |
| } |