1 /*
2  * Copyright (C) 2010 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 
17 package com.example.android.newalarm;
18 
19 import android.app.Notification;
20 import android.app.NotificationManager;
21 import android.app.PendingIntent;
22 import android.app.Service;
23 import android.content.Intent;
24 import android.os.Binder;
25 import android.os.IBinder;
26 import android.os.Parcel;
27 import android.os.RemoteException;
28 import android.widget.Toast;
29 
30 /**
31  * <p>
32  * This class implements a service. The service is started by AlarmActivity, which contains a
33  * repeating countdown timer that sends a PendingIntent. The user starts and stops the timer with
34  * buttons in the UI.
35  * </p>
36  * <p>
37  * When this service is started, it creates a Runnable and starts it in a new Thread. The
38  * Runnable does a synchronized lock on the service's Binder object for 15 seconds, then issues
39  * a stopSelf(). The net effect is a new worker thread that takes 15 seconds to run and then
40  * shuts down the entire service. The activity restarts the service after 15 more seconds, when the
41  * countdown timer triggers again.
42  * </p>
43  * <p>
44  * This service is provided as the service under test for the sample test application
45  * AlarmServiceTest.
46  * </p>
47  * <p>
48  * Note: Since this sample is based on the Android 1.5 platform, it does not implement
49  * onStartCommand. See the Javadoc for android.app.Service for more details.
50  * </p>
51  */
52 public class AlarmService extends Service {
53     // Defines a label for the thread that this service starts
54     private static final String ALARM_SERVICE_THREAD = "AlarmService";
55 
56     // Defines 15 seconds
57     public static final long WAIT_TIME_SECONDS = 15;
58 
59     // Define the number of milliseconds in one second
60     public static final long MILLISECS_PER_SEC = 1000;
61 
62     /*
63      * For testing purposes, the following variables are defined as fields and set to
64      * package visibility.
65      */
66 
67     // The NotificationManager used to send notifications to the status bar.
68     NotificationManager mNotificationManager;
69 
70     // An Intent that displays the client if the user clicks the notification.
71     PendingIntent mContentIntent;
72 
73     // A Notification to send to the Notification Manager when the service is started.
74     Notification mNotification;
75 
76     // A Binder, used as the lock object for the worker thread.
77     IBinder mBinder = new AlarmBinder();
78 
79     // A Thread object that will run the background task
80     Thread mWorkThread;
81 
82     // The Runnable that is the service's "task". This illustrates how a service is used to
83     // offload work from a client.
84     Runnable mWorkTask = new Runnable() {
85         public void run() {
86             // Sets the wait time to 15 seconds, simulating a 15-second background task.
87             long waitTime = System.currentTimeMillis() + WAIT_TIME_SECONDS * MILLISECS_PER_SEC;
88 
89             // Puts the wait in a while loop to ensure that it actually waited 15 seconds.
90             // This covers the situation where an interrupt might have overridden the wait.
91             while (System.currentTimeMillis() < waitTime) {
92                 // Waits for 15 seconds or interruption
93                 synchronized (mBinder) {
94                     try {
95                         // Waits for 15 seconds or until an interrupt triggers an exception.
96                         // If an interrupt occurs, the wait is recalculated to ensure a net
97                         // wait of 15 seconds.
98                         mBinder.wait(waitTime - System.currentTimeMillis());
99                     } catch (InterruptedException e) {
100                     }
101                 }
102             }
103             // Stops the current service. In response, Android calls onDestroy().
104             stopSelf();
105         }
106     };
107 
108     /**
109      *  Makes a full concrete subclass of Binder, rather than doing it in line, for readability.
110      */
111     public class AlarmBinder extends Binder {
112         // Constructor. Calls the super constructor to set up the instance.
AlarmBinder()113         public AlarmBinder() {
114             super();
115         }
116 
117         @Override
onTransact(int code, Parcel data, Parcel reply, int flags)118         protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
119             throws RemoteException {
120 
121             // Call the parent method with the arguments passed in
122             return super.onTransact(code, data, reply, flags);
123         }
124     }
125 
126     /**
127      * Initializes the service when it is first started by a call to startService() or
128      * bindService().
129      */
130     @Override
onCreate()131     public void onCreate() {
132         // Gets a handle to the system mNotification service.
133         mNotificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
134 
135         // Updates the status bar to indicate that this service is running.
136         showNotification();
137 
138         // Creates a new thread. A new thread is used so that the service's work doesn't block
139         // anything on the calling client's thread. By default, a service runs in the same
140         // process and thread as the client that starts it.
141         mWorkThread = new Thread(
142             null,  // threadgroup (in this case, null)
143             mWorkTask, // the Runnable that will run in this thread
144             ALARM_SERVICE_THREAD
145         );
146         // Starts the thread
147         mWorkThread.start();
148     }
149 
150     /**
151      * Stops the service in response to the stopSelf() issued when the wait is over. Other
152      * clients that use this service could stop it by issuing a stopService() or a stopSelf() on
153      * the service object.
154      */
155     @Override
onDestroy()156     public void onDestroy() {
157         // Cancels the status bar mNotification based on its ID, which is set in showNotification().
158         mNotificationManager.cancel(R.string.alarm_service_started);
159 
160         // Sends a notification to the screen.
161         Toast.makeText(
162             this,  // the current context
163             R.string.alarm_service_finished,  // the message to show
164             Toast.LENGTH_LONG   // how long to keep the message on the screen
165         ).show();  // show the text
166     }
167 
168     // Returns the service's binder object to clients that issue onBind().
169     @Override
onBind(Intent intent)170     public IBinder onBind(Intent intent) {
171         return mBinder;
172     }
173 
174     /**
175      * Displays a notification in the status bar that this service is running. This method
176      * also creates an Intent for the AlarmActivity client and attaches it to the notification
177      * line. If the user clicks the line in the expanded status window, the Intent triggers
178      * AlarmActivity.
179      */
showNotification()180     private void showNotification() {
181         // Sets the text to use for the status bar and status list views.
182         CharSequence notificationText = getText(R.string.alarm_service_started);
183 
184         // Sets up the Intent that starts AlarmActivity
185         mContentIntent = PendingIntent.getActivity(
186             this,  // Start the Activity in the current context
187             0,   // not used
188             new Intent(this, AlarmActivity.class),  // A new Intent for AlarmActivity
189             0  // Use an existing activity instance if available
190         );
191 
192         // Build the notification object.
193         mNotification = new Notification.Builder(this)  //  The builder requires the context
194                 .setSmallIcon(R.drawable.stat_sample)  // the status icon
195                 .setTicker(notificationText)  // the status text
196                 .setWhen(System.currentTimeMillis())  // the time stamp
197                 .setContentTitle(getText(R.string.alarm_service_label))  // the label of the entry
198                 .setContentText(notificationText)  // the contents of the entry
199                 .setContentIntent(mContentIntent)  // The intent to send when the entry is clicked
200                 .build();
201 
202         // Sets a unique ID for the notification and sends it to NotificationManager to be
203         // displayed. The ID is the integer marker for the notification string, which is
204         // guaranteed to be unique within the entire application.
205         mNotificationManager.notify(
206             R.string.alarm_service_started,  // unique id for the mNotification
207             mNotification   // the mNotification object
208         );
209     }
210 }
211