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     @Override
    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      */
    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
    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
    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 
    478             public MyServiceConnection() {
    479                 mUnbindOnDisconnect = false;
    480             }
    481 
    482             public MyServiceConnection(boolean unbindOnDisconnect) {
    483                 mUnbindOnDisconnect = unbindOnDisconnect;
    484             }
    485 
    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 
    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
    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