19f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li/* 2f04335f899f2cce69f843692a3cb9cec229683c2tturney * Copyright (C) 2017 The Android Open Source Project 39f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * 4f04335f899f2cce69f843692a3cb9cec229683c2tturney * Licensed under the Apache License, Version 2.0 (the "License"); 5f04335f899f2cce69f843692a3cb9cec229683c2tturney * you may not use this file except in compliance with the License. 6f04335f899f2cce69f843692a3cb9cec229683c2tturney * You may obtain a copy of the License at 79f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * 8f04335f899f2cce69f843692a3cb9cec229683c2tturney * http://www.apache.org/licenses/LICENSE-2.0 99f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * 109f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * Unless required by applicable law or agreed to in writing, software 11f04335f899f2cce69f843692a3cb9cec229683c2tturney * distributed under the License is distributed on an "AS IS" BASIS, 12f04335f899f2cce69f843692a3cb9cec229683c2tturney * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13f04335f899f2cce69f843692a3cb9cec229683c2tturney * See the License for the specific language governing permissions and 14f04335f899f2cce69f843692a3cb9cec229683c2tturney * limitations under the License. 159f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li */ 169f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 179f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Lipackage com.googlecode.android_scripting.activity; 189f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 199f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.app.Notification; 209f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.app.NotificationManager; 219f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.app.PendingIntent; 229f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.content.Context; 239f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.content.Intent; 249f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.content.SharedPreferences; 259f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.os.Binder; 269f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.os.IBinder; 279f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.os.StrictMode; 289f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.preference.PreferenceManager; 299f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 309f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport com.googlecode.android_scripting.AndroidProxy; 319f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport com.googlecode.android_scripting.BaseApplication; 329f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport com.googlecode.android_scripting.Constants; 339f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport com.googlecode.android_scripting.ForegroundService; 349f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport com.googlecode.android_scripting.NotificationIdFactory; 359f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport com.googlecode.android_scripting.R; 369f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport com.googlecode.android_scripting.ScriptLauncher; 379f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport com.googlecode.android_scripting.ScriptProcess; 389f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport com.googlecode.android_scripting.interpreter.InterpreterConfiguration; 399f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport com.googlecode.android_scripting.interpreter.InterpreterProcess; 409f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport com.googlecode.android_scripting.interpreter.shell.ShellInterpreter; 419f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 429f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport org.connectbot.ConsoleActivity; 439f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport org.connectbot.service.TerminalManager; 449f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 459f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.io.File; 469f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.lang.ref.WeakReference; 479f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.net.InetSocketAddress; 489f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.util.ArrayList; 499f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.util.List; 509f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.util.Map; 519f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.util.concurrent.ConcurrentHashMap; 529f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 539f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li/** 549f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * A service that allows scripts and the RPC server to run in the background. 559f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * 569f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li */ 579f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Lipublic class ScriptingLayerService extends ForegroundService { 589f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private static final int NOTIFICATION_ID = NotificationIdFactory.create(); 599f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 609f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private final IBinder mBinder; 619f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private final Map<Integer, InterpreterProcess> mProcessMap; 629f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private final String LOG_TAG = "sl4a"; 639f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private volatile int mModCount = 0; 649f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private NotificationManager mNotificationManager; 659f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private Notification mNotification; 669f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private PendingIntent mNotificationPendingIntent; 679f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private InterpreterConfiguration mInterpreterConfiguration; 689f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 699f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private volatile WeakReference<InterpreterProcess> mRecentlyKilledProcess; 709f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 719f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private TerminalManager mTerminalManager; 729f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 739f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private SharedPreferences mPreferences = null; 749f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private boolean mHide; 759f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 769f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public class LocalBinder extends Binder { 779f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public ScriptingLayerService getService() { 789f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return ScriptingLayerService.this; 799f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 809f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 819f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 829f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li @Override 839f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public IBinder onBind(Intent intent) { 849f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return mBinder; 859f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 869f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 879f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public ScriptingLayerService() { 889f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li super(NOTIFICATION_ID); 899f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li mProcessMap = new ConcurrentHashMap<Integer, InterpreterProcess>(); 909f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li mBinder = new LocalBinder(); 919f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 929f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 939f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li @Override 949f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public void onCreate() { 959f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li super.onCreate(); 969f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li mInterpreterConfiguration = ((BaseApplication) getApplication()).getInterpreterConfiguration(); 979f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 989f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li mRecentlyKilledProcess = new WeakReference<InterpreterProcess>(null); 999f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li mTerminalManager = new TerminalManager(this); 1009f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li mPreferences = PreferenceManager.getDefaultSharedPreferences(this); 1019f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li mHide = mPreferences.getBoolean(Constants.HIDE_NOTIFY, false); 1029f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 1039f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 1049f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li @Override 1059f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li protected Notification createNotification() { 1069f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li Intent notificationIntent = new Intent(this, ScriptingLayerService.class); 1079f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li notificationIntent.setAction(Constants.ACTION_SHOW_RUNNING_SCRIPTS); 1089f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li mNotificationPendingIntent = PendingIntent.getService(this, 0, notificationIntent, 0); 1099f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 1109f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li Notification.Builder builder = new Notification.Builder(this); 1119f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li builder.setSmallIcon(R.drawable.sl4a_notification_logo) 1129f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li .setTicker(null) 1139f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li .setWhen(System.currentTimeMillis()) 1149f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li .setContentTitle("SL4A Service") 1159f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li .setContentText("Tap to view running scripts") 1169f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li .setContentIntent(mNotificationPendingIntent); 1179f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li mNotification = builder.build(); 1189f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li mNotification.flags = Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT; 1199f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return mNotification; 1209f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 1219f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 1229f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private void updateNotification(String tickerText) { 1239f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (tickerText.equals(mNotification.tickerText)) { 1249f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li // Consequent notifications with the same ticker-text are displayed without any ticker-text. 1259f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li // This is a way around. Alternatively, we can display process name and port. 1269f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li tickerText = tickerText + " "; 1279f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 1289f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li String msg; 1299f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (mProcessMap.size() <= 1) { 1309f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li msg = "Tap to view " + Integer.toString(mProcessMap.size()) + " running script"; 1319f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } else { 1329f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li msg = "Tap to view " + Integer.toString(mProcessMap.size()) + " running scripts"; 1339f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 1349f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li Notification.Builder builder = new Notification.Builder(this); 1359f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li builder.setContentTitle("SL4A Service") 1369f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li .setContentText(msg) 1379f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li .setContentIntent(mNotificationPendingIntent) 1389f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li .setSmallIcon(mNotification.icon, mProcessMap.size()) 1399f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li .setWhen(mNotification.when) 1409f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li .setTicker(tickerText); 1419f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 1429f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li mNotification = builder.build(); 1439f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li mNotificationManager.notify(NOTIFICATION_ID, mNotification); 1449f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 1459f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 146b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold private void startAction(Intent intent, int flags, int startId) { 147b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold AndroidProxy proxy = null; 148b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold InterpreterProcess interpreterProcess = null; 1499f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li String errmsg = null; 1509f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (intent == null) { 151b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold } else if (intent.getAction().equals(Constants.ACTION_KILL_ALL)) { 1529f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li killAll(); 1539f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li stopSelf(startId); 154b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold } else if (intent.getAction().equals(Constants.ACTION_KILL_PROCESS)) { 1559f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li killProcess(intent); 1569f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (mProcessMap.isEmpty()) { 1579f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li stopSelf(startId); 1589f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 159b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold } else if (intent.getAction().equals(Constants.ACTION_SHOW_RUNNING_SCRIPTS)) { 1609f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li showRunningScripts(); 161b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold } else { //We are launching a script of some kind 162b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold if (intent.getAction().equals(Constants.ACTION_LAUNCH_SERVER)) { 163b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold proxy = launchServer(intent, false); 164b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold // TODO(damonkohler): This is just to make things easier. Really, we shouldn't need to start 165b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold // an interpreter when all we want is a server. 166b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold interpreterProcess = new InterpreterProcess(new ShellInterpreter(), proxy); 167b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold interpreterProcess.setName("Server"); 168b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold } 169b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold else if (intent.getAction().equals(Constants.ACTION_LAUNCH_FOREGROUND_SCRIPT)) { 170b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold proxy = launchServer(intent, true); 1719f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li launchTerminal(proxy.getAddress()); 1729f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li try { 1739f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li interpreterProcess = launchScript(intent, proxy); 1749f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } catch (RuntimeException e) { 1759f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li errmsg = 1769f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li "Unable to run " + intent.getStringExtra(Constants.EXTRA_SCRIPT_PATH) + "\n" 1779f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li + e.getMessage(); 1789f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li interpreterProcess = null; 1799f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 1809f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } else if (intent.getAction().equals(Constants.ACTION_LAUNCH_BACKGROUND_SCRIPT)) { 181b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold proxy = launchServer(intent, true); 1829f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li interpreterProcess = launchScript(intent, proxy); 1839f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } else if (intent.getAction().equals(Constants.ACTION_LAUNCH_INTERPRETER)) { 184b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold proxy = launchServer(intent, true); 1859f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li launchTerminal(proxy.getAddress()); 1869f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li interpreterProcess = launchInterpreter(intent, proxy); 1879f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 188b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold if (interpreterProcess == null) { 189b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold errmsg = "Action not implemented: " + intent.getAction(); 190b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold } else { 191b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold addProcess(interpreterProcess); 192b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold } 1939f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 1949f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (errmsg != null) { 1959f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li updateNotification(errmsg); 1969f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 197b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold } 198b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold 199b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold @Override 200b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold public int onStartCommand(Intent intent, int flags, int startId) { 201b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold super.onStartCommand(intent, flags, startId); 202b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold 203b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold //TODO: b/26538940 We need to go back to a strict policy and fix the problems 204b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold StrictMode.ThreadPolicy sl4aPolicy = new StrictMode.ThreadPolicy.Builder() 205b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold .detectAll() 206b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold .penaltyLog() 207b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold .build(); 208b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold StrictMode.setThreadPolicy(sl4aPolicy); 209b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold 210b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold Thread launchThread = new Thread(new Runnable() { 211b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold public void run() { 212b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold startAction(intent, flags, startId); 213b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold } 214b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold }); 215b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold launchThread.start(); 216b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold 2179f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return START_REDELIVER_INTENT; 2189f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2199f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 2209f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private boolean tryPort(AndroidProxy androidProxy, boolean usePublicIp, int usePort) { 2219f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (usePublicIp) { 2229f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return (androidProxy.startPublic(usePort) != null); 2239f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } else { 2249f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return (androidProxy.startLocal(usePort) != null); 2259f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2269f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2279f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 2289f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private AndroidProxy launchServer(Intent intent, boolean requiresHandshake) { 2299f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li AndroidProxy androidProxy = new AndroidProxy(this, intent, requiresHandshake); 2309f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li boolean usePublicIp = intent.getBooleanExtra(Constants.EXTRA_USE_EXTERNAL_IP, false); 2319f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li int usePort = intent.getIntExtra(Constants.EXTRA_USE_SERVICE_PORT, 0); 2329f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li // If port is in use, fall back to default behaviour 2339f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (!tryPort(androidProxy, usePublicIp, usePort)) { 2349f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (usePort != 0) { 2359f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li tryPort(androidProxy, usePublicIp, 0); 2369f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2379f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2389f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return androidProxy; 2399f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2409f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 2419f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private ScriptProcess launchScript(Intent intent, AndroidProxy proxy) { 2429f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li final int port = proxy.getAddress().getPort(); 2439f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li File script = new File(intent.getStringExtra(Constants.EXTRA_SCRIPT_PATH)); 2449f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return ScriptLauncher.launchScript(script, mInterpreterConfiguration, proxy, new Runnable() { 2459f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li @Override 2469f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public void run() { 2479f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li // TODO(damonkohler): This action actually kills the script rather than notifying the 2489f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li // service that script exited on its own. We should distinguish between these two cases. 2499f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li Intent intent = new Intent(ScriptingLayerService.this, ScriptingLayerService.class); 2509f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li intent.setAction(Constants.ACTION_KILL_PROCESS); 2519f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li intent.putExtra(Constants.EXTRA_PROXY_PORT, port); 2529f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li startService(intent); 2539f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2549f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li }); 2559f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2569f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 2579f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private InterpreterProcess launchInterpreter(Intent intent, AndroidProxy proxy) { 2589f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li InterpreterConfiguration config = 2599f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li ((BaseApplication) getApplication()).getInterpreterConfiguration(); 2609f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li final int port = proxy.getAddress().getPort(); 2619f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return ScriptLauncher.launchInterpreter(proxy, intent, config, new Runnable() { 2629f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li @Override 2639f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public void run() { 2649f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li // TODO(damonkohler): This action actually kills the script rather than notifying the 2659f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li // service that script exited on its own. We should distinguish between these two cases. 2669f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li Intent intent = new Intent(ScriptingLayerService.this, ScriptingLayerService.class); 2679f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li intent.setAction(Constants.ACTION_KILL_PROCESS); 2689f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li intent.putExtra(Constants.EXTRA_PROXY_PORT, port); 2699f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li startService(intent); 2709f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2719f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li }); 2729f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2739f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 2749f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private void launchTerminal(InetSocketAddress address) { 2759f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li Intent i = new Intent(this, ConsoleActivity.class); 2769f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 2779f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li i.putExtra(Constants.EXTRA_PROXY_PORT, address.getPort()); 2789f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li startActivity(i); 2799f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2809f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 2819f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private void showRunningScripts() { 2829f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li Intent i = new Intent(this, ScriptProcessMonitor.class); 2839f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 2849f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li startActivity(i); 2859f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2869f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 2879f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private void addProcess(InterpreterProcess process) { 288b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold synchronized(mProcessMap) { 289b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold mProcessMap.put(process.getPort(), process); 290b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold mModCount++; 291b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold } 2929f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (!mHide) { 2939f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li updateNotification(process.getName() + " started."); 2949f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2959f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 2969f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 2979f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private InterpreterProcess removeProcess(int port) { 298b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold InterpreterProcess process; 299b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold synchronized(mProcessMap) { 300b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold process = mProcessMap.remove(port); 301b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold if (process == null) { 302b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold return null; 303b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold } 304b3a2a85cfa37a83cf06e42fa6c50e4766e3afb09Nathan Harold mModCount++; 3059f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 3069f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (!mHide) { 3079f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li updateNotification(process.getName() + " exited."); 3089f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 3099f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return process; 3109f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 3119f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 3129f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private void killProcess(Intent intent) { 3139f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li int processId = intent.getIntExtra(Constants.EXTRA_PROXY_PORT, 0); 3149f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li InterpreterProcess process = removeProcess(processId); 3159f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (process != null) { 3169f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li process.kill(); 3179f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li mRecentlyKilledProcess = new WeakReference<InterpreterProcess>(process); 3189f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 3199f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 3209f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 3219f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public int getModCount() { 3229f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return mModCount; 3239f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 3249f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 3259f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li private void killAll() { 3269f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li for (InterpreterProcess process : getScriptProcessesList()) { 3279f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li process = removeProcess(process.getPort()); 3289f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (process != null) { 3299f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li process.kill(); 3309f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 3319f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 3329f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 3339f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 3349f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public List<InterpreterProcess> getScriptProcessesList() { 3359f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li ArrayList<InterpreterProcess> result = new ArrayList<InterpreterProcess>(); 3369f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li result.addAll(mProcessMap.values()); 3379f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return result; 3389f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 3399f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 3409f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public InterpreterProcess getProcess(int port) { 3419f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li InterpreterProcess p = mProcessMap.get(port); 3429f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li if (p == null) { 3439f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return mRecentlyKilledProcess.get(); 3449f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 3459f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return p; 3469f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 3479f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li 3489f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li public TerminalManager getTerminalManager() { 3499f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li return mTerminalManager; 3509f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li } 3519f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li} 352