• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * ConnectBot: simple, powerful, open-source SSH client for Android
3  * Copyright 2007 Kenny Root, Jeffrey Sharkey
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package org.connectbot.service;
19 
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.SharedPreferences;
23 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
24 import android.content.res.AssetFileDescriptor;
25 import android.content.res.Configuration;
26 import android.content.res.Resources;
27 import android.media.AudioManager;
28 import android.media.MediaPlayer;
29 import android.media.MediaPlayer.OnCompletionListener;
30 import android.os.Handler;
31 import android.os.Message;
32 import android.os.Vibrator;
33 import android.preference.PreferenceManager;
34 
35 import com.googlecode.android_scripting.Constants;
36 import com.googlecode.android_scripting.Log;
37 import com.googlecode.android_scripting.R;
38 import com.googlecode.android_scripting.activity.ScriptingLayerService;
39 import com.googlecode.android_scripting.exception.Sl4aException;
40 import com.googlecode.android_scripting.interpreter.InterpreterProcess;
41 
42 import org.connectbot.transport.ProcessTransport;
43 import org.connectbot.util.PreferenceConstants;
44 
45 import java.io.IOException;
46 import java.lang.ref.WeakReference;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.concurrent.ConcurrentHashMap;
50 import java.util.concurrent.CopyOnWriteArrayList;
51 
52 /**
53  * Manager for SSH connections that runs as a background service. This service holds a list of
54  * currently connected SSH bridges that are ready for connection up to a GUI if needed.
55  *
56  * @author jsharkey
57  * @author modified by raaar
58  */
59 public class TerminalManager implements OnSharedPreferenceChangeListener {
60 
61   private static final long VIBRATE_DURATION = 30;
62 
63   private final List<TerminalBridge> bridges = new CopyOnWriteArrayList<TerminalBridge>();
64 
65   private final Map<Integer, WeakReference<TerminalBridge>> mHostBridgeMap =
66       new ConcurrentHashMap<Integer, WeakReference<TerminalBridge>>();
67 
68   private Handler mDisconnectHandler = null;
69 
70   private final Resources mResources;
71 
72   private final SharedPreferences mPreferences;
73 
74   private boolean hardKeyboardHidden;
75 
76   private Vibrator vibrator;
77   private boolean wantKeyVibration;
78   private boolean wantBellVibration;
79   private boolean wantAudible;
80   private boolean resizeAllowed = false;
81   private MediaPlayer mediaPlayer;
82 
83   private final ScriptingLayerService mService;
84 
TerminalManager(ScriptingLayerService service)85   public TerminalManager(ScriptingLayerService service) {
86     mService = service;
87     mPreferences = PreferenceManager.getDefaultSharedPreferences(mService);
88     registerOnSharedPreferenceChangeListener(this);
89     mResources = mService.getResources();
90     hardKeyboardHidden =
91         (mResources.getConfiguration().hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES);
92     vibrator = (Vibrator) mService.getSystemService(Context.VIBRATOR_SERVICE);
93     wantKeyVibration = mPreferences.getBoolean(PreferenceConstants.BUMPY_ARROWS, true);
94     wantBellVibration = mPreferences.getBoolean(PreferenceConstants.BELL_VIBRATE, true);
95     wantAudible = mPreferences.getBoolean(PreferenceConstants.BELL, true);
96     if (wantAudible) {
97       enableMediaPlayer();
98     }
99   }
100 
101   /**
102    * Disconnect all currently connected bridges.
103    */
disconnectAll()104   private void disconnectAll() {
105     TerminalBridge[] bridgesArray = null;
106     if (bridges.size() > 0) {
107       bridgesArray = bridges.toArray(new TerminalBridge[bridges.size()]);
108     }
109     if (bridgesArray != null) {
110       // disconnect and dispose of any existing bridges
111       for (TerminalBridge bridge : bridgesArray) {
112         bridge.dispatchDisconnect(true);
113       }
114     }
115   }
116 
117   /**
118    * Open a new session using the given parameters.
119    *
120    * @throws InterruptedException
121    * @throws Sl4aException
122    */
openConnection(int id)123   public TerminalBridge openConnection(int id) throws IllegalArgumentException, IOException,
124       InterruptedException, Sl4aException {
125     // throw exception if terminal already open
126     if (getConnectedBridge(id) != null) {
127       throw new IllegalArgumentException("Connection already open");
128     }
129 
130     InterpreterProcess process = mService.getProcess(id);
131 
132     TerminalBridge bridge = new TerminalBridge(this, process, new ProcessTransport(process));
133     bridge.connect();
134 
135     WeakReference<TerminalBridge> wr = new WeakReference<TerminalBridge>(bridge);
136     bridges.add(bridge);
137     mHostBridgeMap.put(id, wr);
138 
139     return bridge;
140   }
141 
142   /**
143    * Find a connected {@link TerminalBridge} with the given HostBean.
144    *
145    * @param id
146    *          the HostBean to search for
147    * @return TerminalBridge that uses the HostBean
148    */
getConnectedBridge(int id)149   public TerminalBridge getConnectedBridge(int id) {
150     WeakReference<TerminalBridge> wr = mHostBridgeMap.get(id);
151     if (wr != null) {
152       return wr.get();
153     } else {
154       return null;
155     }
156   }
157 
158   /**
159    * Called by child bridge when somehow it's been disconnected.
160    */
closeConnection(TerminalBridge bridge, boolean killProcess)161   public void closeConnection(TerminalBridge bridge, boolean killProcess) {
162     if (killProcess) {
163       bridges.remove(bridge);
164       mHostBridgeMap.remove(bridge.getId());
165       if (mService.getProcess(bridge.getId()).isAlive()) {
166         Intent intent = new Intent(mService, mService.getClass());
167         intent.setAction(Constants.ACTION_KILL_PROCESS);
168         intent.putExtra(Constants.EXTRA_PROXY_PORT, bridge.getId());
169         mService.startService(intent);
170       }
171     }
172     if (mDisconnectHandler != null) {
173       Message.obtain(mDisconnectHandler, -1, bridge).sendToTarget();
174     }
175   }
176 
177   /**
178    * Allow {@link TerminalBridge} to resize when the parent has changed.
179    *
180    * @param resizeAllowed
181    */
setResizeAllowed(boolean resizeAllowed)182   public void setResizeAllowed(boolean resizeAllowed) {
183     this.resizeAllowed = resizeAllowed;
184   }
185 
isResizeAllowed()186   public boolean isResizeAllowed() {
187     return resizeAllowed;
188   }
189 
stop()190   public void stop() {
191     resizeAllowed = false;
192     disconnectAll();
193     disableMediaPlayer();
194   }
195 
getIntParameter(String key, int defValue)196   public int getIntParameter(String key, int defValue) {
197     return mPreferences.getInt(key, defValue);
198   }
199 
getStringParameter(String key, String defValue)200   public String getStringParameter(String key, String defValue) {
201     return mPreferences.getString(key, defValue);
202   }
203 
tryKeyVibrate()204   public void tryKeyVibrate() {
205     if (wantKeyVibration) {
206       vibrate();
207     }
208   }
209 
vibrate()210   private void vibrate() {
211     if (vibrator != null) {
212       vibrator.vibrate(VIBRATE_DURATION);
213     }
214   }
215 
enableMediaPlayer()216   private void enableMediaPlayer() {
217     mediaPlayer = new MediaPlayer();
218 
219     float volume =
220         mPreferences.getFloat(PreferenceConstants.BELL_VOLUME,
221             PreferenceConstants.DEFAULT_BELL_VOLUME);
222 
223     mediaPlayer.setAudioStreamType(AudioManager.STREAM_NOTIFICATION);
224     mediaPlayer.setOnCompletionListener(new BeepListener());
225 
226     AssetFileDescriptor file = mResources.openRawResourceFd(R.raw.bell);
227     try {
228       mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength());
229       file.close();
230       mediaPlayer.setVolume(volume, volume);
231       mediaPlayer.prepare();
232     } catch (IOException e) {
233       Log.e("Error setting up bell media player", e);
234     }
235   }
236 
disableMediaPlayer()237   private void disableMediaPlayer() {
238     if (mediaPlayer != null) {
239       mediaPlayer.release();
240       mediaPlayer = null;
241     }
242   }
243 
playBeep()244   public void playBeep() {
245     if (mediaPlayer != null) {
246       mediaPlayer.start();
247     }
248     if (wantBellVibration) {
249       vibrate();
250     }
251   }
252 
253   private static class BeepListener implements OnCompletionListener {
onCompletion(MediaPlayer mp)254     public void onCompletion(MediaPlayer mp) {
255       mp.seekTo(0);
256     }
257   }
258 
isHardKeyboardHidden()259   public boolean isHardKeyboardHidden() {
260     return hardKeyboardHidden;
261   }
262 
setHardKeyboardHidden(boolean b)263   public void setHardKeyboardHidden(boolean b) {
264     hardKeyboardHidden = b;
265   }
266 
267   @Override
onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key)268   public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
269     if (PreferenceConstants.BELL.equals(key)) {
270       wantAudible = sharedPreferences.getBoolean(PreferenceConstants.BELL, true);
271       if (wantAudible && mediaPlayer == null) {
272         enableMediaPlayer();
273       } else if (!wantAudible && mediaPlayer != null) {
274         disableMediaPlayer();
275       }
276     } else if (PreferenceConstants.BELL_VOLUME.equals(key)) {
277       if (mediaPlayer != null) {
278         float volume =
279             sharedPreferences.getFloat(PreferenceConstants.BELL_VOLUME,
280                 PreferenceConstants.DEFAULT_BELL_VOLUME);
281         mediaPlayer.setVolume(volume, volume);
282       }
283     } else if (PreferenceConstants.BELL_VIBRATE.equals(key)) {
284       wantBellVibration = sharedPreferences.getBoolean(PreferenceConstants.BELL_VIBRATE, true);
285     } else if (PreferenceConstants.BUMPY_ARROWS.equals(key)) {
286       wantKeyVibration = sharedPreferences.getBoolean(PreferenceConstants.BUMPY_ARROWS, true);
287     }
288   }
289 
setDisconnectHandler(Handler disconnectHandler)290   public void setDisconnectHandler(Handler disconnectHandler) {
291     mDisconnectHandler = disconnectHandler;
292   }
293 
getBridgeList()294   public List<TerminalBridge> getBridgeList() {
295     return bridges;
296   }
297 
getResources()298   public Resources getResources() {
299     return mResources;
300   }
301 
registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener)302   public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
303     mPreferences.registerOnSharedPreferenceChangeListener(listener);
304   }
305 
306 }
307