/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.example.android.apis.app; import android.app.Activity; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.RemoteException; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Process; import android.os.RemoteCallbackList; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; // Need the following import to get access to the app resources, since this // class is in a sub-package. import com.example.android.apis.R; /** * This is an example of implementing an application service that runs in a * different process than the application. Because it can be in another * process, we must use IPC to interact with it. The * {@link Controller} and {@link Binding} classes * show how to interact with the service. * *
Note that most applications do not need to deal with
* the complexity shown here. If your application simply has a service
* running in its own process, the {@link LocalService} sample shows a much
* simpler way to interact with it.
*/
public class RemoteService extends Service {
/**
* This is a list of callbacks that have been registered with the
* service. Note that this is package scoped (instead of private) so
* that it can be accessed more efficiently from inner classes.
*/
final RemoteCallbackList
Note that this is implemented as an inner class only keep the sample * all together; typically this code would appear in some separate class. */ public static class Controller extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.remote_service_controller); // Watch for button clicks. Button button = (Button)findViewById(R.id.start); button.setOnClickListener(mStartListener); button = (Button)findViewById(R.id.stop); button.setOnClickListener(mStopListener); } private OnClickListener mStartListener = new OnClickListener() { public void onClick(View v) { // Make sure the service is started. It will continue running // until someone calls stopService(). // We use an action code here, instead of explictly supplying // the component name, so that other packages can replace // the service. startService(new Intent( "com.example.android.apis.app.REMOTE_SERVICE")); } }; private OnClickListener mStopListener = new OnClickListener() { public void onClick(View v) { // Cancel a previous call to startService(). Note that the // service will not actually stop at this point if there are // still bound clients. stopService(new Intent( "com.example.android.apis.app.REMOTE_SERVICE")); } }; } // ---------------------------------------------------------------------- /** * Example of binding and unbinding to the remote service. * This demonstrates the implementation of a service which the client will * bind to, interacting with it through an aidl interface.
* *Note that this is implemented as an inner class only keep the sample * all together; typically this code would appear in some separate class. */ // BEGIN_INCLUDE(calling_a_service) public static class Binding extends Activity { /** The primary interface we will be calling on the service. */ IRemoteService mService = null; /** Another interface we use on the service. */ ISecondary mSecondaryService = null; Button mKillButton; TextView mCallbackText; private boolean mIsBound; /** * Standard initialization of this activity. Set up the UI, then wait * for the user to poke it before doing anything. */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.remote_service_binding); // Watch for button clicks. Button button = (Button)findViewById(R.id.bind); button.setOnClickListener(mBindListener); button = (Button)findViewById(R.id.unbind); button.setOnClickListener(mUnbindListener); mKillButton = (Button)findViewById(R.id.kill); mKillButton.setOnClickListener(mKillListener); mKillButton.setEnabled(false); mCallbackText = (TextView)findViewById(R.id.callback); mCallbackText.setText("Not attached."); } /** * Class for interacting with the main interface of the service. */ private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { // This is called when the connection with the service has been // established, giving us the service object we can use to // interact with the service. We are communicating with our // service through an IDL interface, so get a client-side // representation of that from the raw service object. mService = IRemoteService.Stub.asInterface(service); mKillButton.setEnabled(true); mCallbackText.setText("Attached."); // We want to monitor the service for as long as we are // connected to it. try { mService.registerCallback(mCallback); } catch (RemoteException e) { // In this case the service has crashed before we could even // do anything with it; we can count on soon being // disconnected (and then reconnected if it can be restarted) // so there is no need to do anything here. } // As part of the sample, tell the user what happened. Toast.makeText(Binding.this, R.string.remote_service_connected, Toast.LENGTH_SHORT).show(); } public void onServiceDisconnected(ComponentName className) { // This is called when the connection with the service has been // unexpectedly disconnected -- that is, its process crashed. mService = null; mKillButton.setEnabled(false); mCallbackText.setText("Disconnected."); // As part of the sample, tell the user what happened. Toast.makeText(Binding.this, R.string.remote_service_disconnected, Toast.LENGTH_SHORT).show(); } }; /** * Class for interacting with the secondary interface of the service. */ private ServiceConnection mSecondaryConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { // Connecting to a secondary interface is the same as any // other interface. mSecondaryService = ISecondary.Stub.asInterface(service); mKillButton.setEnabled(true); } public void onServiceDisconnected(ComponentName className) { mSecondaryService = null; mKillButton.setEnabled(false); } }; private OnClickListener mBindListener = new OnClickListener() { public void onClick(View v) { // Establish a couple connections with the service, binding // by interface names. This allows other applications to be // installed that replace the remote service by implementing // the same interface. bindService(new Intent(IRemoteService.class.getName()), mConnection, Context.BIND_AUTO_CREATE); bindService(new Intent(ISecondary.class.getName()), mSecondaryConnection, Context.BIND_AUTO_CREATE); mIsBound = true; mCallbackText.setText("Binding."); } }; private OnClickListener mUnbindListener = new OnClickListener() { public void onClick(View v) { if (mIsBound) { // If we have received the service, and hence registered with // it, then now is the time to unregister. if (mService != null) { try { mService.unregisterCallback(mCallback); } catch (RemoteException e) { // There is nothing special we need to do if the service // has crashed. } } // Detach our existing connection. unbindService(mConnection); unbindService(mSecondaryConnection); mKillButton.setEnabled(false); mIsBound = false; mCallbackText.setText("Unbinding."); } } }; private OnClickListener mKillListener = new OnClickListener() { public void onClick(View v) { // To kill the process hosting our service, we need to know its // PID. Conveniently our service has a call that will return // to us that information. if (mSecondaryService != null) { try { int pid = mSecondaryService.getPid(); // Note that, though this API allows us to request to // kill any process based on its PID, the kernel will // still impose standard restrictions on which PIDs you // are actually able to kill. Typically this means only // the process running your application and any additional // processes created by that app as shown here; packages // sharing a common UID will also be able to kill each // other's processes. Process.killProcess(pid); mCallbackText.setText("Killed service process."); } catch (RemoteException ex) { // Recover gracefully from the process hosting the // server dying. // Just for purposes of the sample, put up a notification. Toast.makeText(Binding.this, R.string.remote_call_failed, Toast.LENGTH_SHORT).show(); } } } }; // ---------------------------------------------------------------------- // Code showing how to deal with callbacks. // ---------------------------------------------------------------------- /** * This implementation is used to receive callbacks from the remote * service. */ private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() { /** * This is called by the remote service regularly to tell us about * new values. Note that IPC calls are dispatched through a thread * pool running in each process, so the code executing here will * NOT be running in our main thread like most other things -- so, * to update the UI, we need to use a Handler to hop over there. */ public void valueChanged(int value) { mHandler.sendMessage(mHandler.obtainMessage(BUMP_MSG, value, 0)); } }; private static final int BUMP_MSG = 1; private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case BUMP_MSG: mCallbackText.setText("Received from service: " + msg.arg1); break; default: super.handleMessage(msg); } } }; } // END_INCLUDE(calling_a_service) // ---------------------------------------------------------------------- /** * Examples of behavior of different bind flags.
*/ // BEGIN_INCLUDE(calling_a_service) public static class BindingOptions extends Activity { ServiceConnection mCurConnection; TextView mCallbackText; class MyServiceConnection implements ServiceConnection { final boolean mUnbindOnDisconnect; public MyServiceConnection() { mUnbindOnDisconnect = false; } public MyServiceConnection(boolean unbindOnDisconnect) { mUnbindOnDisconnect = unbindOnDisconnect; } public void onServiceConnected(ComponentName className, IBinder service) { if (mCurConnection != this) { return; } mCallbackText.setText("Attached."); Toast.makeText(BindingOptions.this, R.string.remote_service_connected, Toast.LENGTH_SHORT).show(); } public void onServiceDisconnected(ComponentName className) { if (mCurConnection != this) { return; } mCallbackText.setText("Disconnected."); Toast.makeText(BindingOptions.this, R.string.remote_service_disconnected, Toast.LENGTH_SHORT).show(); if (mUnbindOnDisconnect) { unbindService(this); mCurConnection = null; Toast.makeText(BindingOptions.this, R.string.remote_service_unbind_disconn, Toast.LENGTH_SHORT).show(); } } } /** * Standard initialization of this activity. Set up the UI, then wait * for the user to poke it before doing anything. */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.remote_binding_options); // Watch for button clicks. Button button = (Button)findViewById(R.id.bind_normal); button.setOnClickListener(mBindNormalListener); button = (Button)findViewById(R.id.bind_not_foreground); button.setOnClickListener(mBindNotForegroundListener); button = (Button)findViewById(R.id.bind_above_client); button.setOnClickListener(mBindAboveClientListener); button = (Button)findViewById(R.id.bind_allow_oom); button.setOnClickListener(mBindAllowOomListener); button = (Button)findViewById(R.id.bind_waive_priority); button.setOnClickListener(mBindWaivePriorityListener); button = (Button)findViewById(R.id.bind_important); button.setOnClickListener(mBindImportantListener); button = (Button)findViewById(R.id.bind_with_activity); button.setOnClickListener(mBindWithActivityListener); button = (Button)findViewById(R.id.unbind); button.setOnClickListener(mUnbindListener); mCallbackText = (TextView)findViewById(R.id.callback); mCallbackText.setText("Not attached."); } private OnClickListener mBindNormalListener = new OnClickListener() { public void onClick(View v) { if (mCurConnection != null) { unbindService(mCurConnection); mCurConnection = null; } ServiceConnection conn = new MyServiceConnection(); if (bindService(new Intent(IRemoteService.class.getName()), conn, Context.BIND_AUTO_CREATE)) { mCurConnection = conn; } } }; private OnClickListener mBindNotForegroundListener = new OnClickListener() { public void onClick(View v) { if (mCurConnection != null) { unbindService(mCurConnection); mCurConnection = null; } ServiceConnection conn = new MyServiceConnection(); if (bindService(new Intent(IRemoteService.class.getName()), conn, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND)) { mCurConnection = conn; } } }; private OnClickListener mBindAboveClientListener = new OnClickListener() { public void onClick(View v) { if (mCurConnection != null) { unbindService(mCurConnection); mCurConnection = null; } ServiceConnection conn = new MyServiceConnection(); if (bindService(new Intent(IRemoteService.class.getName()), conn, Context.BIND_AUTO_CREATE | Context.BIND_ABOVE_CLIENT)) { mCurConnection = conn; } } }; private OnClickListener mBindAllowOomListener = new OnClickListener() { public void onClick(View v) { if (mCurConnection != null) { unbindService(mCurConnection); mCurConnection = null; } ServiceConnection conn = new MyServiceConnection(); if (bindService(new Intent(IRemoteService.class.getName()), conn, Context.BIND_AUTO_CREATE | Context.BIND_ALLOW_OOM_MANAGEMENT)) { mCurConnection = conn; } } }; private OnClickListener mBindWaivePriorityListener = new OnClickListener() { public void onClick(View v) { if (mCurConnection != null) { unbindService(mCurConnection); mCurConnection = null; } ServiceConnection conn = new MyServiceConnection(true); if (bindService(new Intent(IRemoteService.class.getName()), conn, Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY)) { mCurConnection = conn; } } }; private OnClickListener mBindImportantListener = new OnClickListener() { public void onClick(View v) { if (mCurConnection != null) { unbindService(mCurConnection); mCurConnection = null; } ServiceConnection conn = new MyServiceConnection(); if (bindService(new Intent(IRemoteService.class.getName()), conn, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT)) { mCurConnection = conn; } } }; private OnClickListener mBindWithActivityListener = new OnClickListener() { public void onClick(View v) { if (mCurConnection != null) { unbindService(mCurConnection); mCurConnection = null; } ServiceConnection conn = new MyServiceConnection(); if (bindService(new Intent(IRemoteService.class.getName()), conn, Context.BIND_AUTO_CREATE | Context.BIND_ADJUST_WITH_ACTIVITY | Context.BIND_WAIVE_PRIORITY)) { mCurConnection = conn; } } }; private OnClickListener mUnbindListener = new OnClickListener() { public void onClick(View v) { if (mCurConnection != null) { unbindService(mCurConnection); mCurConnection = null; } } }; } }