1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 
17 package com.android.tools.sdkcontroller.activities;
18 
19 import android.app.Activity;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.ServiceConnection;
24 import android.os.IBinder;
25 import android.util.Log;
26 
27 import com.android.tools.sdkcontroller.service.ControllerService;
28 import com.android.tools.sdkcontroller.service.ControllerService.ControllerBinder;
29 import com.android.tools.sdkcontroller.service.ControllerService.ControllerListener;
30 
31 /**
32  * Base activity class that knows how to bind and unbind from the
33  * {@link ControllerService}.
34  */
35 public abstract class BaseBindingActivity extends Activity {
36 
37     public static String TAG = BaseBindingActivity.class.getSimpleName();
38     private static boolean DEBUG = true;
39     private ServiceConnection mServiceConnection;
40     private ControllerBinder mServiceBinder;
41 
42     /**
43      * Returns the binder. Activities can use that to query the controller service.
44      * @return An existing {@link ControllerBinder}.
45      *   The binder is only valid between calls {@link #onServiceConnected()} and
46      *   {@link #onServiceDisconnected()}. Returns null when not valid.
47      */
getServiceBinder()48     public ControllerBinder getServiceBinder() {
49         return mServiceBinder;
50     }
51 
52     /**
53      * Called when the activity resumes.
54      * This automatically binds to the service, starting it as needed.
55      * <p/>
56      * Since on resume we automatically bind to the service, the {@link ServiceConnection}
57      * will is restored and {@link #onServiceConnected()} is called as necessary.
58      * Derived classes that need to initialize anything that is related to the service
59      * (e.g. getting their handler) should thus do so in {@link #onServiceConnected()} and
60      * <em>not</em> in {@link #onResume()} -- since binding to the service is asynchronous
61      * there is <em>no</em> guarantee that {@link #getServiceBinder()} returns non-null
62      * when this call finishes.
63      */
64     @Override
onResume()65     protected void onResume() {
66         super.onResume();
67         bindToService();
68     }
69 
70     /**
71      * Called when the activity is paused.
72      * This automatically unbinds from the service but does not stop it.
73      */
74     @Override
onPause()75     protected void onPause() {
76         super.onPause();
77         unbindFromService();
78     }
79 
80     // ----------
81 
82     /**
83      * Called when binding to the service to get the activity's {@link ControllerListener}.
84      * @return A new non-null {@link ControllerListener}.
85      */
createControllerListener()86     protected abstract ControllerListener createControllerListener();
87 
88     /**
89      * Called by the service once the activity is connected (bound) to it.
90      * <p/>
91      * When this is called, {@link #getServiceBinder()} returns a non-null binder that
92      * can be used by the activity to control the service.
93      */
onServiceConnected()94     protected abstract void onServiceConnected();
95 
96     /**
97      * Called by the service when it is forcibly disconnected OR when we know
98      * we're unbinding the service.
99      * <p/>
100      * When this is called, {@link #getServiceBinder()} returns a null binder and
101      * the activity should stop using that binder and remove any reference to it.
102      */
onServiceDisconnected()103     protected abstract void onServiceDisconnected();
104 
105     /**
106      * Starts the service and binds to it.
107      */
bindToService()108     protected void bindToService() {
109         if (mServiceConnection == null) {
110             final ControllerListener listener = createControllerListener();
111 
112             mServiceConnection = new ServiceConnection() {
113                 /**
114                  * Called when the service is connected.
115                  * Allows us to retrieve the binder to talk to the service.
116                  */
117                 @Override
118                 public void onServiceConnected(ComponentName name, IBinder service) {
119                     if (DEBUG) Log.d(TAG, "Activity connected to service");
120                     mServiceBinder = (ControllerBinder) service;
121                     mServiceBinder.addControllerListener(listener);
122                     BaseBindingActivity.this.onServiceConnected();
123                 }
124 
125                 /**
126                  * Called when the service got disconnected, e.g. because it crashed.
127                  * This is <em>not</em> called when we unbind from the service.
128                  */
129                 @Override
130                 public void onServiceDisconnected(ComponentName name) {
131                     if (DEBUG) Log.d(TAG, "Activity disconnected from service");
132                     mServiceBinder = null;
133                     BaseBindingActivity.this.onServiceDisconnected();
134                 }
135             };
136         }
137 
138         // Start service so that it doesn't stop when we unbind
139         if (DEBUG) Log.d(TAG, "start requested & bind service");
140         Intent service = new Intent(this, ControllerService.class);
141         startService(service);
142         bindService(service,
143                 mServiceConnection,
144                 Context.BIND_AUTO_CREATE);
145     }
146 
147     /**
148      * Unbinds from the service but does not actually stop the service.
149      * This lets us have it run in the background even if this isn't the active activity.
150      */
unbindFromService()151     protected void unbindFromService() {
152         if (mServiceConnection != null) {
153             if (DEBUG) Log.d(TAG, "unbind service");
154             mServiceConnection.onServiceDisconnected(null /*name*/);
155             unbindService(mServiceConnection);
156             mServiceConnection = null;
157         }
158     }
159 }