• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
startAlarm(Context context, AlarmInstance instance)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      */
stopAlarm(Context context, AlarmInstance instance)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 
startAlarm(AlarmInstance instance)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 
stopCurrentAlarm()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
onCreate()128     public void onCreate() {
129         super.onCreate();
130         mTelephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
131     }
132 
133     @Override
onStartCommand(Intent intent, int flags, int startId)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
onDestroy()166     public void onDestroy() {
167         Log.v("AlarmService.onDestroy() called");
168         super.onDestroy();
169         stopCurrentAlarm();
170     }
171 
172     @Override
onBind(Intent intent)173     public IBinder onBind(Intent intent) {
174         return null;
175     }
176 }
177