Home | History | Annotate | Download | only in app
      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
     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
     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
    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     private static final int REPORT_MSG = 1;
    139 
    140     /**
    141      * Our Handler used to execute operations on the main thread.  This is used
    142      * to schedule increments of our value.
    143      */
    144     private final Handler mHandler = new Handler() {
    145         @Override public void handleMessage(Message msg) {
    146             switch (msg.what) {
    147 
    148                 // It is time to bump the value!
    149                 case REPORT_MSG: {
    150                     // Up it goes.
    151                     int value = ++mValue;
    152 
    153                     // Broadcast to all clients the new value.
    154                     final int N = mCallbacks.beginBroadcast();
    155                     for (int i=0; i<N; i++) {
    156                         try {
    157                             mCallbacks.getBroadcastItem(i).valueChanged(value);
    158                         } catch (RemoteException e) {
    159                             // The RemoteCallbackList will take care of removing
    160                             // the dead object for us.
    161                         }
    162                     }
    163                     mCallbacks.finishBroadcast();
    164 
    165                     // Repeat every 1 second.
    166                     sendMessageDelayed(obtainMessage(REPORT_MSG), 1*1000);
    167                 } break;
    168                 default:
    169                     super.handleMessage(msg);
    170             }
    171         }
    172     };
    173 
    174     /**
    175      * Show a notification while this service is running.
    176      */
    177     private void showNotification() {
    178         // In this sample, we'll use the same text for the ticker and the expanded notification
    179         CharSequence text = getText(R.string.remote_service_started);
    180 
    181         // Set the icon, scrolling text and timestamp
    182         Notification notification = new Notification(R.drawable.stat_sample, text,
    183                 System.currentTimeMillis());
    184 
    185         // The PendingIntent to launch our activity if the user selects this notification
    186         PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
    187                 new Intent(this, Controller.class), 0);
    188 
    189         // Set the info for the views that show in the notification panel.
    190         notification.setLatestEventInfo(this, getText(R.string.remote_service_label),
    191                        text, contentIntent);
    192 
    193         // Send the notification.
    194         // We use a string id because it is a unique number.  We use it later to cancel.
    195         mNM.notify(R.string.remote_service_started, notification);
    196     }
    197 
    198     // ----------------------------------------------------------------------
    199 
    200     /**
    201      * <p>Example of explicitly starting and stopping the remove service.
    202      * This demonstrates the implementation of a service that runs in a different
    203      * process than the rest of the application, which is explicitly started and stopped
    204      * as desired.</p>
    205      *
    206      * <p>Note that this is implemented as an inner class only keep the sample
    207      * all together; typically this code would appear in some separate class.
    208      */
    209     public static class Controller extends Activity {
    210         @Override
    211         protected void onCreate(Bundle savedInstanceState) {
    212             super.onCreate(savedInstanceState);
    213 
    214             setContentView(R.layout.remote_service_controller);
    215 
    216             // Watch for button clicks.
    217             Button button = (Button)findViewById(R.id.start);
    218             button.setOnClickListener(mStartListener);
    219             button = (Button)findViewById(R.id.stop);
    220             button.setOnClickListener(mStopListener);
    221         }
    222 
    223         private OnClickListener mStartListener = new OnClickListener() {
    224             public void onClick(View v) {
    225                 // Make sure the service is started.  It will continue running
    226                 // until someone calls stopService().
    227                 // We use an action code here, instead of explictly supplying
    228                 // the component name, so that other packages can replace
    229                 // the service.
    230                 startService(new Intent(
    231                         "com.example.android.apis.app.REMOTE_SERVICE"));
    232             }
    233         };
    234 
    235         private OnClickListener mStopListener = new OnClickListener() {
    236             public void onClick(View v) {
    237                 // Cancel a previous call to startService().  Note that the
    238                 // service will not actually stop at this point if there are
    239                 // still bound clients.
    240                 stopService(new Intent(
    241                         "com.example.android.apis.app.REMOTE_SERVICE"));
    242             }
    243         };
    244     }
    245 
    246     // ----------------------------------------------------------------------
    247 
    248     /**
    249      * Example of binding and unbinding to the remote service.
    250      * This demonstrates the implementation of a service which the client will
    251      * bind to, interacting with it through an aidl interface.</p>
    252      *
    253      * <p>Note that this is implemented as an inner class only keep the sample
    254      * all together; typically this code would appear in some separate class.
    255      */
    256  // BEGIN_INCLUDE(calling_a_service)
    257     public static class Binding extends Activity {
    258         /** The primary interface we will be calling on the service. */
    259         IRemoteService mService = null;
    260         /** Another interface we use on the service. */
    261         ISecondary mSecondaryService = null;
    262 
    263         Button mKillButton;
    264         TextView mCallbackText;
    265 
    266         private boolean mIsBound;
    267 
    268         /**
    269          * Standard initialization of this activity.  Set up the UI, then wait
    270          * for the user to poke it before doing anything.
    271          */
    272         @Override
    273         protected void onCreate(Bundle savedInstanceState) {
    274             super.onCreate(savedInstanceState);
    275 
    276             setContentView(R.layout.remote_service_binding);
    277 
    278             // Watch for button clicks.
    279             Button button = (Button)findViewById(R.id.bind);
    280             button.setOnClickListener(mBindListener);
    281             button = (Button)findViewById(R.id.unbind);
    282             button.setOnClickListener(mUnbindListener);
    283             mKillButton = (Button)findViewById(R.id.kill);
    284             mKillButton.setOnClickListener(mKillListener);
    285             mKillButton.setEnabled(false);
    286 
    287             mCallbackText = (TextView)findViewById(R.id.callback);
    288             mCallbackText.setText("Not attached.");
    289         }
    290 
    291         /**
    292          * Class for interacting with the main interface of the service.
    293          */
    294         private ServiceConnection mConnection = new ServiceConnection() {
    295             public void onServiceConnected(ComponentName className,
    296                     IBinder service) {
    297                 // This is called when the connection with the service has been
    298                 // established, giving us the service object we can use to
    299                 // interact with the service.  We are communicating with our
    300                 // service through an IDL interface, so get a client-side
    301                 // representation of that from the raw service object.
    302                 mService = IRemoteService.Stub.asInterface(service);
    303                 mKillButton.setEnabled(true);
    304                 mCallbackText.setText("Attached.");
    305 
    306                 // We want to monitor the service for as long as we are
    307                 // connected to it.
    308                 try {
    309                     mService.registerCallback(mCallback);
    310                 } catch (RemoteException e) {
    311                     // In this case the service has crashed before we could even
    312                     // do anything with it; we can count on soon being
    313                     // disconnected (and then reconnected if it can be restarted)
    314                     // so there is no need to do anything here.
    315                 }
    316 
    317                 // As part of the sample, tell the user what happened.
    318                 Toast.makeText(Binding.this, R.string.remote_service_connected,
    319                         Toast.LENGTH_SHORT).show();
    320             }
    321 
    322             public void onServiceDisconnected(ComponentName className) {
    323                 // This is called when the connection with the service has been
    324                 // unexpectedly disconnected -- that is, its process crashed.
    325                 mService = null;
    326                 mKillButton.setEnabled(false);
    327                 mCallbackText.setText("Disconnected.");
    328 
    329                 // As part of the sample, tell the user what happened.
    330                 Toast.makeText(Binding.this, R.string.remote_service_disconnected,
    331                         Toast.LENGTH_SHORT).show();
    332             }
    333         };
    334 
    335         /**
    336          * Class for interacting with the secondary interface of the service.
    337          */
    338         private ServiceConnection mSecondaryConnection = new ServiceConnection() {
    339             public void onServiceConnected(ComponentName className,
    340                     IBinder service) {
    341                 // Connecting to a secondary interface is the same as any
    342                 // other interface.
    343                 mSecondaryService = ISecondary.Stub.asInterface(service);
    344                 mKillButton.setEnabled(true);
    345             }
    346 
    347             public void onServiceDisconnected(ComponentName className) {
    348                 mSecondaryService = null;
    349                 mKillButton.setEnabled(false);
    350             }
    351         };
    352 
    353         private OnClickListener mBindListener = new OnClickListener() {
    354             public void onClick(View v) {
    355                 // Establish a couple connections with the service, binding
    356                 // by interface names.  This allows other applications to be
    357                 // installed that replace the remote service by implementing
    358                 // the same interface.
    359                 bindService(new Intent(IRemoteService.class.getName()),
    360                         mConnection, Context.BIND_AUTO_CREATE);
    361                 bindService(new Intent(ISecondary.class.getName()),
    362                         mSecondaryConnection, Context.BIND_AUTO_CREATE);
    363                 mIsBound = true;
    364                 mCallbackText.setText("Binding.");
    365             }
    366         };
    367 
    368         private OnClickListener mUnbindListener = new OnClickListener() {
    369             public void onClick(View v) {
    370                 if (mIsBound) {
    371                     // If we have received the service, and hence registered with
    372                     // it, then now is the time to unregister.
    373                     if (mService != null) {
    374                         try {
    375                             mService.unregisterCallback(mCallback);
    376                         } catch (RemoteException e) {
    377                             // There is nothing special we need to do if the service
    378                             // has crashed.
    379                         }
    380                     }
    381 
    382                     // Detach our existing connection.
    383                     unbindService(mConnection);
    384                     unbindService(mSecondaryConnection);
    385                     mKillButton.setEnabled(false);
    386                     mIsBound = false;
    387                     mCallbackText.setText("Unbinding.");
    388                 }
    389             }
    390         };
    391 
    392         private OnClickListener mKillListener = new OnClickListener() {
    393             public void onClick(View v) {
    394                 // To kill the process hosting our service, we need to know its
    395                 // PID.  Conveniently our service has a call that will return
    396                 // to us that information.
    397                 if (mSecondaryService != null) {
    398                     try {
    399                         int pid = mSecondaryService.getPid();
    400                         // Note that, though this API allows us to request to
    401                         // kill any process based on its PID, the kernel will
    402                         // still impose standard restrictions on which PIDs you
    403                         // are actually able to kill.  Typically this means only
    404                         // the process running your application and any additional
    405                         // processes created by that app as shown here; packages
    406                         // sharing a common UID will also be able to kill each
    407                         // other's processes.
    408                         Process.killProcess(pid);
    409                         mCallbackText.setText("Killed service process.");
    410                     } catch (RemoteException ex) {
    411                         // Recover gracefully from the process hosting the
    412                         // server dying.
    413                         // Just for purposes of the sample, put up a notification.
    414                         Toast.makeText(Binding.this,
    415                                 R.string.remote_call_failed,
    416                                 Toast.LENGTH_SHORT).show();
    417                     }
    418                 }
    419             }
    420         };
    421 
    422         // ----------------------------------------------------------------------
    423         // Code showing how to deal with callbacks.
    424         // ----------------------------------------------------------------------
    425 
    426         /**
    427          * This implementation is used to receive callbacks from the remote
    428          * service.
    429          */
    430         private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() {
    431             /**
    432              * This is called by the remote service regularly to tell us about
    433              * new values.  Note that IPC calls are dispatched through a thread
    434              * pool running in each process, so the code executing here will
    435              * NOT be running in our main thread like most other things -- so,
    436              * to update the UI, we need to use a Handler to hop over there.
    437              */
    438             public void valueChanged(int value) {
    439                 mHandler.sendMessage(mHandler.obtainMessage(BUMP_MSG, value, 0));
    440             }
    441         };
    442 
    443         private static final int BUMP_MSG = 1;
    444 
    445         private Handler mHandler = new Handler() {
    446             @Override public void handleMessage(Message msg) {
    447                 switch (msg.what) {
    448                     case BUMP_MSG:
    449                         mCallbackText.setText("Received from service: " + msg.arg1);
    450                         break;
    451                     default:
    452                         super.handleMessage(msg);
    453                 }
    454             }
    455 
    456         };
    457     }
    458 // END_INCLUDE(calling_a_service)
    459 }
    460