• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Activity;
20 import android.app.Notification;
21 import android.app.NotificationManager;
22 import android.app.PendingIntent;
23 import android.app.Service;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.ServiceConnection;
28 import android.os.Bundle;
29 import android.os.RemoteException;
30 import android.os.Handler;
31 import android.os.IBinder;
32 import android.os.Message;
33 import android.os.Process;
34 import android.os.RemoteCallbackList;
35 import android.view.View;
36 import android.view.View.OnClickListener;
37 import android.widget.Button;
38 import android.widget.TextView;
39 import android.widget.Toast;
40 
41 // Need the following import to get access to the app resources, since this
42 // class is in a sub-package.
43 import com.example.android.apis.R;
44 
45 /**
46  * This is an example of implementing an application service that runs in a
47  * different process than the application.  Because it can be in another
48  * process, we must use IPC to interact with it.  The
49  * {@link Controller} and {@link Binding} classes
50  * show how to interact with the service.
51  *
52  * <p>Note that most applications <strong>do not</strong> need to deal with
53  * the complexity shown here.  If your application simply has a service
54  * running in its own process, the {@link LocalService} sample shows a much
55  * simpler way to interact with it.
56  */
57 public class RemoteService extends Service {
58     /**
59      * This is a list of callbacks that have been registered with the
60      * service.  Note that this is package scoped (instead of private) so
61      * that it can be accessed more efficiently from inner classes.
62      */
63     final RemoteCallbackList<IRemoteServiceCallback> mCallbacks
64             = new RemoteCallbackList<IRemoteServiceCallback>();
65 
66     int mValue = 0;
67     NotificationManager mNM;
68 
69     @Override
onCreate()70     public void onCreate() {
71         mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
72 
73         // Display a notification about us starting.
74         showNotification();
75 
76         // While this service is running, it will continually increment a
77         // number.  Send the first message that is used to perform the
78         // increment.
79         mHandler.sendEmptyMessage(REPORT_MSG);
80     }
81 
82     @Override
onDestroy()83     public void onDestroy() {
84         // Cancel the persistent notification.
85         mNM.cancel(R.string.remote_service_started);
86 
87         // Tell the user we stopped.
88         Toast.makeText(this, R.string.remote_service_stopped, Toast.LENGTH_SHORT).show();
89 
90         // Unregister all callbacks.
91         mCallbacks.kill();
92 
93         // Remove the next pending message to increment the counter, stopping
94         // the increment loop.
95         mHandler.removeMessages(REPORT_MSG);
96     }
97 
98 // BEGIN_INCLUDE(exposing_a_service)
99     @Override
onBind(Intent intent)100     public IBinder onBind(Intent intent) {
101         // Select the interface to return.  If your service only implements
102         // a single interface, you can just return it here without checking
103         // the Intent.
104         if (IRemoteService.class.getName().equals(intent.getAction())) {
105             return mBinder;
106         }
107         if (ISecondary.class.getName().equals(intent.getAction())) {
108             return mSecondaryBinder;
109         }
110         return null;
111     }
112 
113     /**
114      * The IRemoteInterface is defined through IDL
115      */
116     private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
117         public void registerCallback(IRemoteServiceCallback cb) {
118             if (cb != null) mCallbacks.register(cb);
119         }
120         public void unregisterCallback(IRemoteServiceCallback cb) {
121             if (cb != null) mCallbacks.unregister(cb);
122         }
123     };
124 
125     /**
126      * A secondary interface to the service.
127      */
128     private final ISecondary.Stub mSecondaryBinder = new ISecondary.Stub() {
129         public int getPid() {
130             return Process.myPid();
131         }
132         public void basicTypes(int anInt, long aLong, boolean aBoolean,
133                 float aFloat, double aDouble, String aString) {
134         }
135     };
136 // END_INCLUDE(exposing_a_service)
137 
138     @Override
onTaskRemoved(Intent rootIntent)139     public void onTaskRemoved(Intent rootIntent) {
140         Toast.makeText(this, "Task removed: " + rootIntent, Toast.LENGTH_LONG).show();
141     }
142 
143     private static final int REPORT_MSG = 1;
144 
145     /**
146      * Our Handler used to execute operations on the main thread.  This is used
147      * to schedule increments of our value.
148      */
149     private final Handler mHandler = new Handler() {
150         @Override public void handleMessage(Message msg) {
151             switch (msg.what) {
152 
153                 // It is time to bump the value!
154                 case REPORT_MSG: {
155                     // Up it goes.
156                     int value = ++mValue;
157 
158                     // Broadcast to all clients the new value.
159                     final int N = mCallbacks.beginBroadcast();
160                     for (int i=0; i<N; i++) {
161                         try {
162                             mCallbacks.getBroadcastItem(i).valueChanged(value);
163                         } catch (RemoteException e) {
164                             // The RemoteCallbackList will take care of removing
165                             // the dead object for us.
166                         }
167                     }
168                     mCallbacks.finishBroadcast();
169 
170                     // Repeat every 1 second.
171                     sendMessageDelayed(obtainMessage(REPORT_MSG), 1*1000);
172                 } break;
173                 default:
174                     super.handleMessage(msg);
175             }
176         }
177     };
178 
179     /**
180      * Show a notification while this service is running.
181      */
showNotification()182     private void showNotification() {
183         // In this sample, we'll use the same text for the ticker and the expanded notification
184         CharSequence text = getText(R.string.remote_service_started);
185 
186         // Set the icon, scrolling text and timestamp
187         Notification notification = new Notification(R.drawable.stat_sample, text,
188                 System.currentTimeMillis());
189 
190         // The PendingIntent to launch our activity if the user selects this notification
191         PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
192                 new Intent(this, Controller.class), 0);
193 
194         // Set the info for the views that show in the notification panel.
195         notification.setLatestEventInfo(this, getText(R.string.remote_service_label),
196                        text, contentIntent);
197 
198         // Send the notification.
199         // We use a string id because it is a unique number.  We use it later to cancel.
200         mNM.notify(R.string.remote_service_started, notification);
201     }
202 
203     // ----------------------------------------------------------------------
204 
205     /**
206      * <p>Example of explicitly starting and stopping the remove service.
207      * This demonstrates the implementation of a service that runs in a different
208      * process than the rest of the application, which is explicitly started and stopped
209      * as desired.</p>
210      *
211      * <p>Note that this is implemented as an inner class only keep the sample
212      * all together; typically this code would appear in some separate class.
213      */
214     public static class Controller extends Activity {
215         @Override
onCreate(Bundle savedInstanceState)216         protected void onCreate(Bundle savedInstanceState) {
217             super.onCreate(savedInstanceState);
218 
219             setContentView(R.layout.remote_service_controller);
220 
221             // Watch for button clicks.
222             Button button = (Button)findViewById(R.id.start);
223             button.setOnClickListener(mStartListener);
224             button = (Button)findViewById(R.id.stop);
225             button.setOnClickListener(mStopListener);
226         }
227 
228         private OnClickListener mStartListener = new OnClickListener() {
229             public void onClick(View v) {
230                 // Make sure the service is started.  It will continue running
231                 // until someone calls stopService().
232                 // We use an action code here, instead of explictly supplying
233                 // the component name, so that other packages can replace
234                 // the service.
235                 startService(new Intent(
236                         "com.example.android.apis.app.REMOTE_SERVICE"));
237             }
238         };
239 
240         private OnClickListener mStopListener = new OnClickListener() {
241             public void onClick(View v) {
242                 // Cancel a previous call to startService().  Note that the
243                 // service will not actually stop at this point if there are
244                 // still bound clients.
245                 stopService(new Intent(
246                         "com.example.android.apis.app.REMOTE_SERVICE"));
247             }
248         };
249     }
250 
251     // ----------------------------------------------------------------------
252 
253     /**
254      * Example of binding and unbinding to the remote service.
255      * This demonstrates the implementation of a service which the client will
256      * bind to, interacting with it through an aidl interface.</p>
257      *
258      * <p>Note that this is implemented as an inner class only keep the sample
259      * all together; typically this code would appear in some separate class.
260      */
261  // BEGIN_INCLUDE(calling_a_service)
262     public static class Binding extends Activity {
263         /** The primary interface we will be calling on the service. */
264         IRemoteService mService = null;
265         /** Another interface we use on the service. */
266         ISecondary mSecondaryService = null;
267 
268         Button mKillButton;
269         TextView mCallbackText;
270 
271         private boolean mIsBound;
272 
273         /**
274          * Standard initialization of this activity.  Set up the UI, then wait
275          * for the user to poke it before doing anything.
276          */
277         @Override
onCreate(Bundle savedInstanceState)278         protected void onCreate(Bundle savedInstanceState) {
279             super.onCreate(savedInstanceState);
280 
281             setContentView(R.layout.remote_service_binding);
282 
283             // Watch for button clicks.
284             Button button = (Button)findViewById(R.id.bind);
285             button.setOnClickListener(mBindListener);
286             button = (Button)findViewById(R.id.unbind);
287             button.setOnClickListener(mUnbindListener);
288             mKillButton = (Button)findViewById(R.id.kill);
289             mKillButton.setOnClickListener(mKillListener);
290             mKillButton.setEnabled(false);
291 
292             mCallbackText = (TextView)findViewById(R.id.callback);
293             mCallbackText.setText("Not attached.");
294         }
295 
296         /**
297          * Class for interacting with the main interface of the service.
298          */
299         private ServiceConnection mConnection = new ServiceConnection() {
300             public void onServiceConnected(ComponentName className,
301                     IBinder service) {
302                 // This is called when the connection with the service has been
303                 // established, giving us the service object we can use to
304                 // interact with the service.  We are communicating with our
305                 // service through an IDL interface, so get a client-side
306                 // representation of that from the raw service object.
307                 mService = IRemoteService.Stub.asInterface(service);
308                 mKillButton.setEnabled(true);
309                 mCallbackText.setText("Attached.");
310 
311                 // We want to monitor the service for as long as we are
312                 // connected to it.
313                 try {
314                     mService.registerCallback(mCallback);
315                 } catch (RemoteException e) {
316                     // In this case the service has crashed before we could even
317                     // do anything with it; we can count on soon being
318                     // disconnected (and then reconnected if it can be restarted)
319                     // so there is no need to do anything here.
320                 }
321 
322                 // As part of the sample, tell the user what happened.
323                 Toast.makeText(Binding.this, R.string.remote_service_connected,
324                         Toast.LENGTH_SHORT).show();
325             }
326 
327             public void onServiceDisconnected(ComponentName className) {
328                 // This is called when the connection with the service has been
329                 // unexpectedly disconnected -- that is, its process crashed.
330                 mService = null;
331                 mKillButton.setEnabled(false);
332                 mCallbackText.setText("Disconnected.");
333 
334                 // As part of the sample, tell the user what happened.
335                 Toast.makeText(Binding.this, R.string.remote_service_disconnected,
336                         Toast.LENGTH_SHORT).show();
337             }
338         };
339 
340         /**
341          * Class for interacting with the secondary interface of the service.
342          */
343         private ServiceConnection mSecondaryConnection = new ServiceConnection() {
344             public void onServiceConnected(ComponentName className,
345                     IBinder service) {
346                 // Connecting to a secondary interface is the same as any
347                 // other interface.
348                 mSecondaryService = ISecondary.Stub.asInterface(service);
349                 mKillButton.setEnabled(true);
350             }
351 
352             public void onServiceDisconnected(ComponentName className) {
353                 mSecondaryService = null;
354                 mKillButton.setEnabled(false);
355             }
356         };
357 
358         private OnClickListener mBindListener = new OnClickListener() {
359             public void onClick(View v) {
360                 // Establish a couple connections with the service, binding
361                 // by interface names.  This allows other applications to be
362                 // installed that replace the remote service by implementing
363                 // the same interface.
364                 bindService(new Intent(IRemoteService.class.getName()),
365                         mConnection, Context.BIND_AUTO_CREATE);
366                 bindService(new Intent(ISecondary.class.getName()),
367                         mSecondaryConnection, Context.BIND_AUTO_CREATE);
368                 mIsBound = true;
369                 mCallbackText.setText("Binding.");
370             }
371         };
372 
373         private OnClickListener mUnbindListener = new OnClickListener() {
374             public void onClick(View v) {
375                 if (mIsBound) {
376                     // If we have received the service, and hence registered with
377                     // it, then now is the time to unregister.
378                     if (mService != null) {
379                         try {
380                             mService.unregisterCallback(mCallback);
381                         } catch (RemoteException e) {
382                             // There is nothing special we need to do if the service
383                             // has crashed.
384                         }
385                     }
386 
387                     // Detach our existing connection.
388                     unbindService(mConnection);
389                     unbindService(mSecondaryConnection);
390                     mKillButton.setEnabled(false);
391                     mIsBound = false;
392                     mCallbackText.setText("Unbinding.");
393                 }
394             }
395         };
396 
397         private OnClickListener mKillListener = new OnClickListener() {
398             public void onClick(View v) {
399                 // To kill the process hosting our service, we need to know its
400                 // PID.  Conveniently our service has a call that will return
401                 // to us that information.
402                 if (mSecondaryService != null) {
403                     try {
404                         int pid = mSecondaryService.getPid();
405                         // Note that, though this API allows us to request to
406                         // kill any process based on its PID, the kernel will
407                         // still impose standard restrictions on which PIDs you
408                         // are actually able to kill.  Typically this means only
409                         // the process running your application and any additional
410                         // processes created by that app as shown here; packages
411                         // sharing a common UID will also be able to kill each
412                         // other's processes.
413                         Process.killProcess(pid);
414                         mCallbackText.setText("Killed service process.");
415                     } catch (RemoteException ex) {
416                         // Recover gracefully from the process hosting the
417                         // server dying.
418                         // Just for purposes of the sample, put up a notification.
419                         Toast.makeText(Binding.this,
420                                 R.string.remote_call_failed,
421                                 Toast.LENGTH_SHORT).show();
422                     }
423                 }
424             }
425         };
426 
427         // ----------------------------------------------------------------------
428         // Code showing how to deal with callbacks.
429         // ----------------------------------------------------------------------
430 
431         /**
432          * This implementation is used to receive callbacks from the remote
433          * service.
434          */
435         private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() {
436             /**
437              * This is called by the remote service regularly to tell us about
438              * new values.  Note that IPC calls are dispatched through a thread
439              * pool running in each process, so the code executing here will
440              * NOT be running in our main thread like most other things -- so,
441              * to update the UI, we need to use a Handler to hop over there.
442              */
443             public void valueChanged(int value) {
444                 mHandler.sendMessage(mHandler.obtainMessage(BUMP_MSG, value, 0));
445             }
446         };
447 
448         private static final int BUMP_MSG = 1;
449 
450         private Handler mHandler = new Handler() {
451             @Override public void handleMessage(Message msg) {
452                 switch (msg.what) {
453                     case BUMP_MSG:
454                         mCallbackText.setText("Received from service: " + msg.arg1);
455                         break;
456                     default:
457                         super.handleMessage(msg);
458                 }
459             }
460 
461         };
462     }
463 // END_INCLUDE(calling_a_service)
464 
465     // ----------------------------------------------------------------------
466 
467     /**
468      * Examples of behavior of different bind flags.</p>
469      */
470  // BEGIN_INCLUDE(calling_a_service)
471     public static class BindingOptions extends Activity {
472         ServiceConnection mCurConnection;
473         TextView mCallbackText;
474 
475         class MyServiceConnection implements ServiceConnection {
476             final boolean mUnbindOnDisconnect;
477 
MyServiceConnection()478             public MyServiceConnection() {
479                 mUnbindOnDisconnect = false;
480             }
481 
MyServiceConnection(boolean unbindOnDisconnect)482             public MyServiceConnection(boolean unbindOnDisconnect) {
483                 mUnbindOnDisconnect = unbindOnDisconnect;
484             }
485 
onServiceConnected(ComponentName className, IBinder service)486             public void onServiceConnected(ComponentName className,
487                     IBinder service) {
488                 if (mCurConnection != this) {
489                     return;
490                 }
491                 mCallbackText.setText("Attached.");
492                 Toast.makeText(BindingOptions.this, R.string.remote_service_connected,
493                         Toast.LENGTH_SHORT).show();
494             }
495 
onServiceDisconnected(ComponentName className)496             public void onServiceDisconnected(ComponentName className) {
497                 if (mCurConnection != this) {
498                     return;
499                 }
500                 mCallbackText.setText("Disconnected.");
501                 Toast.makeText(BindingOptions.this, R.string.remote_service_disconnected,
502                         Toast.LENGTH_SHORT).show();
503                 if (mUnbindOnDisconnect) {
504                     unbindService(this);
505                     mCurConnection = null;
506                     Toast.makeText(BindingOptions.this, R.string.remote_service_unbind_disconn,
507                             Toast.LENGTH_SHORT).show();
508                 }
509             }
510         }
511 
512         /**
513          * Standard initialization of this activity.  Set up the UI, then wait
514          * for the user to poke it before doing anything.
515          */
516         @Override
onCreate(Bundle savedInstanceState)517         protected void onCreate(Bundle savedInstanceState) {
518             super.onCreate(savedInstanceState);
519 
520             setContentView(R.layout.remote_binding_options);
521 
522             // Watch for button clicks.
523             Button button = (Button)findViewById(R.id.bind_normal);
524             button.setOnClickListener(mBindNormalListener);
525             button = (Button)findViewById(R.id.bind_not_foreground);
526             button.setOnClickListener(mBindNotForegroundListener);
527             button = (Button)findViewById(R.id.bind_above_client);
528             button.setOnClickListener(mBindAboveClientListener);
529             button = (Button)findViewById(R.id.bind_allow_oom);
530             button.setOnClickListener(mBindAllowOomListener);
531             button = (Button)findViewById(R.id.bind_waive_priority);
532             button.setOnClickListener(mBindWaivePriorityListener);
533             button = (Button)findViewById(R.id.bind_important);
534             button.setOnClickListener(mBindImportantListener);
535             button = (Button)findViewById(R.id.bind_with_activity);
536             button.setOnClickListener(mBindWithActivityListener);
537             button = (Button)findViewById(R.id.unbind);
538             button.setOnClickListener(mUnbindListener);
539 
540             mCallbackText = (TextView)findViewById(R.id.callback);
541             mCallbackText.setText("Not attached.");
542         }
543 
544         private OnClickListener mBindNormalListener = new OnClickListener() {
545             public void onClick(View v) {
546                 if (mCurConnection != null) {
547                     unbindService(mCurConnection);
548                     mCurConnection = null;
549                 }
550                 ServiceConnection conn = new MyServiceConnection();
551                 if (bindService(new Intent(IRemoteService.class.getName()),
552                         conn, Context.BIND_AUTO_CREATE)) {
553                     mCurConnection = conn;
554                 }
555             }
556         };
557 
558         private OnClickListener mBindNotForegroundListener = new OnClickListener() {
559             public void onClick(View v) {
560                 if (mCurConnection != null) {
561                     unbindService(mCurConnection);
562                     mCurConnection = null;
563                 }
564                 ServiceConnection conn = new MyServiceConnection();
565                 if (bindService(new Intent(IRemoteService.class.getName()),
566                         conn, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND)) {
567                     mCurConnection = conn;
568                 }
569             }
570         };
571 
572         private OnClickListener mBindAboveClientListener = new OnClickListener() {
573             public void onClick(View v) {
574                 if (mCurConnection != null) {
575                     unbindService(mCurConnection);
576                     mCurConnection = null;
577                 }
578                 ServiceConnection conn = new MyServiceConnection();
579                 if (bindService(new Intent(IRemoteService.class.getName()),
580                         conn, Context.BIND_AUTO_CREATE | Context.BIND_ABOVE_CLIENT)) {
581                     mCurConnection = conn;
582                 }
583             }
584         };
585 
586         private OnClickListener mBindAllowOomListener = new OnClickListener() {
587             public void onClick(View v) {
588                 if (mCurConnection != null) {
589                     unbindService(mCurConnection);
590                     mCurConnection = null;
591                 }
592                 ServiceConnection conn = new MyServiceConnection();
593                 if (bindService(new Intent(IRemoteService.class.getName()),
594                         conn, Context.BIND_AUTO_CREATE | Context.BIND_ALLOW_OOM_MANAGEMENT)) {
595                     mCurConnection = conn;
596                 }
597             }
598         };
599 
600         private OnClickListener mBindWaivePriorityListener = new OnClickListener() {
601             public void onClick(View v) {
602                 if (mCurConnection != null) {
603                     unbindService(mCurConnection);
604                     mCurConnection = null;
605                 }
606                 ServiceConnection conn = new MyServiceConnection(true);
607                 if (bindService(new Intent(IRemoteService.class.getName()),
608                         conn, Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY)) {
609                     mCurConnection = conn;
610                 }
611             }
612         };
613 
614         private OnClickListener mBindImportantListener = new OnClickListener() {
615             public void onClick(View v) {
616                 if (mCurConnection != null) {
617                     unbindService(mCurConnection);
618                     mCurConnection = null;
619                 }
620                 ServiceConnection conn = new MyServiceConnection();
621                 if (bindService(new Intent(IRemoteService.class.getName()),
622                         conn, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT)) {
623                     mCurConnection = conn;
624                 }
625             }
626         };
627 
628         private OnClickListener mBindWithActivityListener = new OnClickListener() {
629             public void onClick(View v) {
630                 if (mCurConnection != null) {
631                     unbindService(mCurConnection);
632                     mCurConnection = null;
633                 }
634                 ServiceConnection conn = new MyServiceConnection();
635                 if (bindService(new Intent(IRemoteService.class.getName()),
636                         conn, Context.BIND_AUTO_CREATE | Context.BIND_ADJUST_WITH_ACTIVITY
637                         | Context.BIND_WAIVE_PRIORITY)) {
638                     mCurConnection = conn;
639                 }
640             }
641         };
642 
643         private OnClickListener mUnbindListener = new OnClickListener() {
644             public void onClick(View v) {
645                 if (mCurConnection != null) {
646                     unbindService(mCurConnection);
647                     mCurConnection = null;
648                 }
649             }
650         };
651     }
652 }
653