1 /* 2 * Copyright (C) 2007 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.apis.app; 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.Bundle; 25 import android.os.Handler; 26 import android.os.HandlerThread; 27 import android.os.IBinder; 28 import android.os.Looper; 29 import android.os.Message; 30 import android.os.Process; 31 import android.util.Log; 32 import android.widget.Toast; 33 34 import com.example.android.apis.R; 35 36 /** 37 * This is an example of implementing an application service that runs locally 38 * in the same process as the application. The {@link ServiceStartArgumentsController} 39 * class shows how to interact with the service. 40 * 41 * <p>Notice the use of the {@link NotificationManager} when interesting things 42 * happen in the service. This is generally how background services should 43 * interact with the user, rather than doing something more disruptive such as 44 * calling startActivity(). 45 * 46 * <p>For applications targeting Android 1.5 or beyond, you may want consider 47 * using the android.app.IntentService class, which takes care of all the 48 * work of creating the extra thread and dispatching commands to it. 49 */ 50 public class ServiceStartArguments extends Service { 51 private NotificationManager mNM; 52 private Intent mInvokeIntent; 53 private volatile Looper mServiceLooper; 54 private volatile ServiceHandler mServiceHandler; 55 56 private final class ServiceHandler extends Handler { ServiceHandler(Looper looper)57 public ServiceHandler(Looper looper) { 58 super(looper); 59 } 60 61 @Override handleMessage(Message msg)62 public void handleMessage(Message msg) 63 { 64 Bundle arguments = (Bundle)msg.obj; 65 66 String txt = arguments.getString("name"); 67 68 Log.i("ServiceStartArguments", "Message: " + msg + ", " 69 + arguments.getString("name")); 70 71 if ((msg.arg2&Service.START_FLAG_REDELIVERY) == 0) { 72 txt = "New cmd #" + msg.arg1 + ": " + txt; 73 } else { 74 txt = "Re-delivered #" + msg.arg1 + ": " + txt; 75 } 76 77 showNotification(txt); 78 79 // Normally we would do some work here... for our sample, we will 80 // just sleep for 5 seconds. 81 long endTime = System.currentTimeMillis() + 5*1000; 82 while (System.currentTimeMillis() < endTime) { 83 synchronized (this) { 84 try { 85 wait(endTime - System.currentTimeMillis()); 86 } catch (Exception e) { 87 } 88 } 89 } 90 91 hideNotification(); 92 93 Log.i("ServiceStartArguments", "Done with #" + msg.arg1); 94 stopSelf(msg.arg1); 95 } 96 97 }; 98 99 @Override onCreate()100 public void onCreate() { 101 mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); 102 103 Toast.makeText(this, R.string.service_created, 104 Toast.LENGTH_SHORT).show(); 105 106 // This is who should be launched if the user selects our persistent 107 // notification. 108 mInvokeIntent = new Intent(this, ServiceStartArgumentsController.class); 109 110 // Start up the thread running the service. Note that we create a 111 // separate thread because the service normally runs in the process's 112 // main thread, which we don't want to block. We also make it 113 // background priority so CPU-intensive work will not disrupt our UI. 114 HandlerThread thread = new HandlerThread("ServiceStartArguments", 115 Process.THREAD_PRIORITY_BACKGROUND); 116 thread.start(); 117 118 mServiceLooper = thread.getLooper(); 119 mServiceHandler = new ServiceHandler(mServiceLooper); 120 } 121 122 @Override onStartCommand(Intent intent, int flags, int startId)123 public int onStartCommand(Intent intent, int flags, int startId) { 124 Log.i("ServiceStartArguments", 125 "Starting #" + startId + ": " + intent.getExtras()); 126 Message msg = mServiceHandler.obtainMessage(); 127 msg.arg1 = startId; 128 msg.arg2 = flags; 129 msg.obj = intent.getExtras(); 130 mServiceHandler.sendMessage(msg); 131 Log.i("ServiceStartArguments", "Sending: " + msg); 132 133 // For the start fail button, we will simulate the process dying 134 // for some reason in onStartCommand(). 135 if (intent.getBooleanExtra("fail", false)) { 136 // Don't do this if we are in a retry... the system will 137 // eventually give up if we keep crashing. 138 if ((flags&START_FLAG_RETRY) == 0) { 139 // Since the process hasn't finished handling the command, 140 // it will be restarted with the command again, regardless of 141 // whether we return START_REDELIVER_INTENT. 142 Process.killProcess(Process.myPid()); 143 } 144 } 145 146 // Normally we would consistently return one kind of result... 147 // however, here we will select between these two, so you can see 148 // how they impact the behavior. Try killing the process while it 149 // is in the middle of executing the different commands. 150 return intent.getBooleanExtra("redeliver", false) 151 ? START_REDELIVER_INTENT : START_NOT_STICKY; 152 } 153 154 @Override onDestroy()155 public void onDestroy() { 156 mServiceLooper.quit(); 157 158 hideNotification(); 159 160 // Tell the user we stopped. 161 Toast.makeText(ServiceStartArguments.this, R.string.service_destroyed, 162 Toast.LENGTH_SHORT).show(); 163 } 164 165 @Override onBind(Intent intent)166 public IBinder onBind(Intent intent) { 167 return null; 168 } 169 170 /** 171 * Show a notification while this service is running. 172 */ showNotification(String text)173 private void showNotification(String text) { 174 // Set the icon, scrolling text and timestamp 175 Notification notification = new Notification(R.drawable.stat_sample, text, 176 System.currentTimeMillis()); 177 178 // The PendingIntent to launch our activity if the user selects this notification 179 PendingIntent contentIntent = PendingIntent.getActivity(this, 0, 180 new Intent(this, AlarmService.class), 0); 181 182 // Set the info for the views that show in the notification panel. 183 notification.setLatestEventInfo(this, getText(R.string.service_start_arguments_label), 184 text, contentIntent); 185 186 // We show this for as long as our service is processing a command. 187 notification.flags |= Notification.FLAG_ONGOING_EVENT; 188 189 // Send the notification. 190 // We use a string id because it is a unique number. We use it later to cancel. 191 mNM.notify(R.string.service_created, notification); 192 } 193 hideNotification()194 private void hideNotification() { 195 mNM.cancel(R.string.service_created); 196 } 197 } 198 199