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. 113 public AlarmBinder() { 114 super(); 115 } 116 117 @Override 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 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 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 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 */ 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