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 com.example.android.apis.R; 20 21 import android.app.Activity; 22 import android.content.ComponentName; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.ServiceConnection; 26 import android.os.Bundle; 27 import android.os.RemoteException; 28 import android.os.Handler; 29 import android.os.IBinder; 30 import android.os.Message; 31 import android.os.Process; 32 import android.view.View; 33 import android.view.View.OnClickListener; 34 import android.widget.Button; 35 import android.widget.TextView; 36 import android.widget.Toast; 37 38 // BEGIN_INCLUDE(exposing_a_service) 39 public class RemoteServiceBinding extends Activity { 40 /** The primary interface we will be calling on the service. */ 41 IRemoteService mService = null; 42 /** Another interface we use on the service. */ 43 ISecondary mSecondaryService = null; 44 45 Button mKillButton; 46 TextView mCallbackText; 47 48 private boolean mIsBound; 49 50 /** 51 * Standard initialization of this activity. Set up the UI, then wait 52 * for the user to poke it before doing anything. 53 */ 54 @Override onCreate(Bundle savedInstanceState)55 protected void onCreate(Bundle savedInstanceState) { 56 super.onCreate(savedInstanceState); 57 58 setContentView(R.layout.remote_service_binding); 59 60 // Watch for button clicks. 61 Button button = (Button)findViewById(R.id.bind); 62 button.setOnClickListener(mBindListener); 63 button = (Button)findViewById(R.id.unbind); 64 button.setOnClickListener(mUnbindListener); 65 mKillButton = (Button)findViewById(R.id.kill); 66 mKillButton.setOnClickListener(mKillListener); 67 mKillButton.setEnabled(false); 68 69 mCallbackText = (TextView)findViewById(R.id.callback); 70 mCallbackText.setText("Not attached."); 71 } 72 73 /** 74 * Class for interacting with the main interface of the service. 75 */ 76 private ServiceConnection mConnection = new ServiceConnection() { 77 public void onServiceConnected(ComponentName className, 78 IBinder service) { 79 // This is called when the connection with the service has been 80 // established, giving us the service object we can use to 81 // interact with the service. We are communicating with our 82 // service through an IDL interface, so get a client-side 83 // representation of that from the raw service object. 84 mService = IRemoteService.Stub.asInterface(service); 85 mKillButton.setEnabled(true); 86 mCallbackText.setText("Attached."); 87 88 // We want to monitor the service for as long as we are 89 // connected to it. 90 try { 91 mService.registerCallback(mCallback); 92 } catch (RemoteException e) { 93 // In this case the service has crashed before we could even 94 // do anything with it; we can count on soon being 95 // disconnected (and then reconnected if it can be restarted) 96 // so there is no need to do anything here. 97 } 98 99 // As part of the sample, tell the user what happened. 100 Toast.makeText(RemoteServiceBinding.this, R.string.remote_service_connected, 101 Toast.LENGTH_SHORT).show(); 102 } 103 104 public void onServiceDisconnected(ComponentName className) { 105 // This is called when the connection with the service has been 106 // unexpectedly disconnected -- that is, its process crashed. 107 mService = null; 108 mKillButton.setEnabled(false); 109 mCallbackText.setText("Disconnected."); 110 111 // As part of the sample, tell the user what happened. 112 Toast.makeText(RemoteServiceBinding.this, R.string.remote_service_disconnected, 113 Toast.LENGTH_SHORT).show(); 114 } 115 }; 116 117 /** 118 * Class for interacting with the secondary interface of the service. 119 */ 120 private ServiceConnection mSecondaryConnection = new ServiceConnection() { 121 public void onServiceConnected(ComponentName className, 122 IBinder service) { 123 // Connecting to a secondary interface is the same as any 124 // other interface. 125 mSecondaryService = ISecondary.Stub.asInterface(service); 126 mKillButton.setEnabled(true); 127 } 128 129 public void onServiceDisconnected(ComponentName className) { 130 mSecondaryService = null; 131 mKillButton.setEnabled(false); 132 } 133 }; 134 135 private OnClickListener mBindListener = new OnClickListener() { 136 public void onClick(View v) { 137 // Establish a couple connections with the service, binding 138 // by interface names. This allows other applications to be 139 // installed that replace the remote service by implementing 140 // the same interface. 141 bindService(new Intent(IRemoteService.class.getName()), 142 mConnection, Context.BIND_AUTO_CREATE); 143 bindService(new Intent(ISecondary.class.getName()), 144 mSecondaryConnection, Context.BIND_AUTO_CREATE); 145 mIsBound = true; 146 mCallbackText.setText("Binding."); 147 } 148 }; 149 150 private OnClickListener mUnbindListener = new OnClickListener() { 151 public void onClick(View v) { 152 if (mIsBound) { 153 // If we have received the service, and hence registered with 154 // it, then now is the time to unregister. 155 if (mService != null) { 156 try { 157 mService.unregisterCallback(mCallback); 158 } catch (RemoteException e) { 159 // There is nothing special we need to do if the service 160 // has crashed. 161 } 162 } 163 164 // Detach our existing connection. 165 unbindService(mConnection); 166 unbindService(mSecondaryConnection); 167 mKillButton.setEnabled(false); 168 mIsBound = false; 169 mCallbackText.setText("Unbinding."); 170 } 171 } 172 }; 173 174 private OnClickListener mKillListener = new OnClickListener() { 175 public void onClick(View v) { 176 // To kill the process hosting our service, we need to know its 177 // PID. Conveniently our service has a call that will return 178 // to us that information. 179 if (mSecondaryService != null) { 180 try { 181 int pid = mSecondaryService.getPid(); 182 // Note that, though this API allows us to request to 183 // kill any process based on its PID, the kernel will 184 // still impose standard restrictions on which PIDs you 185 // are actually able to kill. Typically this means only 186 // the process running your application and any additional 187 // processes created by that app as shown here; packages 188 // sharing a common UID will also be able to kill each 189 // other's processes. 190 Process.killProcess(pid); 191 mCallbackText.setText("Killed service process."); 192 } catch (RemoteException ex) { 193 // Recover gracefully from the process hosting the 194 // server dying. 195 // Just for purposes of the sample, put up a notification. 196 Toast.makeText(RemoteServiceBinding.this, 197 R.string.remote_call_failed, 198 Toast.LENGTH_SHORT).show(); 199 } 200 } 201 } 202 }; 203 204 // ---------------------------------------------------------------------- 205 // Code showing how to deal with callbacks. 206 // ---------------------------------------------------------------------- 207 208 /** 209 * This implementation is used to receive callbacks from the remote 210 * service. 211 */ 212 private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() { 213 /** 214 * This is called by the remote service regularly to tell us about 215 * new values. Note that IPC calls are dispatched through a thread 216 * pool running in each process, so the code executing here will 217 * NOT be running in our main thread like most other things -- so, 218 * to update the UI, we need to use a Handler to hop over there. 219 */ 220 public void valueChanged(int value) { 221 mHandler.sendMessage(mHandler.obtainMessage(BUMP_MSG, value, 0)); 222 } 223 }; 224 225 private static final int BUMP_MSG = 1; 226 227 private Handler mHandler = new Handler() { 228 @Override public void handleMessage(Message msg) { 229 switch (msg.what) { 230 case BUMP_MSG: 231 mCallbackText.setText("Received from service: " + msg.arg1); 232 break; 233 default: 234 super.handleMessage(msg); 235 } 236 } 237 238 }; 239 } 240 // END_INCLUDE(exposing_a_service) 241 242