1 /* 2 * Copyright (C) 2017 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.googlecode.android_scripting; 18 19 import android.content.Context; 20 import android.content.SharedPreferences; 21 import android.os.AsyncTask; 22 import android.os.Handler; 23 import android.os.Looper; 24 import android.preference.PreferenceManager; 25 26 import com.googlecode.android_scripting.exception.Sl4aException; 27 import com.googlecode.android_scripting.interpreter.InterpreterConstants; 28 import com.googlecode.android_scripting.interpreter.InterpreterDescriptor; 29 import com.googlecode.android_scripting.interpreter.InterpreterUtils; 30 31 import java.io.File; 32 import java.net.MalformedURLException; 33 import java.util.ArrayList; 34 import java.util.LinkedList; 35 import java.util.List; 36 import java.util.Queue; 37 38 /** 39 * AsyncTask for installing interpreters. 40 * 41 */ 42 public abstract class InterpreterInstaller extends AsyncTask<Void, Void, Boolean> { 43 44 protected final InterpreterDescriptor mDescriptor; 45 protected final AsyncTaskListener<Boolean> mTaskListener; 46 protected final Queue<RequestCode> mTaskQueue; 47 protected final Context mContext; 48 49 protected final Handler mainThreadHandler; 50 protected Handler mBackgroundHandler; 51 52 protected volatile AsyncTask<Void, Integer, Long> mTaskHolder; 53 54 protected final String mInterpreterRoot; 55 56 protected static enum RequestCode { 57 DOWNLOAD_INTERPRETER, DOWNLOAD_INTERPRETER_EXTRAS, DOWNLOAD_SCRIPTS, EXTRACT_INTERPRETER, 58 EXTRACT_INTERPRETER_EXTRAS, EXTRACT_SCRIPTS 59 } 60 61 // Executed in the UI thread. 62 private final Runnable mTaskStarter = new Runnable() { 63 @Override 64 public void run() { 65 RequestCode task = mTaskQueue.peek(); 66 try { 67 AsyncTask<Void, Integer, Long> newTask = null; 68 switch (task) { 69 case DOWNLOAD_INTERPRETER: 70 newTask = downloadInterpreter(); 71 break; 72 case DOWNLOAD_INTERPRETER_EXTRAS: 73 newTask = downloadInterpreterExtras(); 74 break; 75 case DOWNLOAD_SCRIPTS: 76 newTask = downloadScripts(); 77 break; 78 case EXTRACT_INTERPRETER: 79 newTask = extractInterpreter(); 80 break; 81 case EXTRACT_INTERPRETER_EXTRAS: 82 newTask = extractInterpreterExtras(); 83 break; 84 case EXTRACT_SCRIPTS: 85 newTask = extractScripts(); 86 break; 87 } 88 mTaskHolder = newTask.execute(); 89 } catch (Exception e) { 90 Log.v(e.getMessage(), e); 91 } 92 93 if (mBackgroundHandler != null) { 94 mBackgroundHandler.post(mTaskWorker); 95 } 96 } 97 }; 98 99 // Executed in the background. 100 private final Runnable mTaskWorker = new Runnable() { 101 @Override 102 public void run() { 103 RequestCode request = mTaskQueue.peek(); 104 try { 105 if (mTaskHolder != null && mTaskHolder.get() != null) { 106 mTaskQueue.remove(); 107 mTaskHolder = null; 108 // Post processing. 109 if (request == RequestCode.EXTRACT_INTERPRETER && !chmodIntepreter()) { 110 // Chmod returned false. 111 Looper.myLooper().quit(); 112 } else if (mTaskQueue.size() == 0) { 113 // We're done here. 114 Looper.myLooper().quit(); 115 return; 116 } else if (mainThreadHandler != null) { 117 // There's still some work to do. 118 mainThreadHandler.post(mTaskStarter); 119 return; 120 } 121 } 122 } catch (Exception e) { 123 Log.e(e); 124 } 125 // Something went wrong... 126 switch (request) { 127 case DOWNLOAD_INTERPRETER: 128 Log.e("Downloading interpreter failed."); 129 break; 130 case DOWNLOAD_INTERPRETER_EXTRAS: 131 Log.e("Downloading interpreter extras failed."); 132 break; 133 case DOWNLOAD_SCRIPTS: 134 Log.e("Downloading scripts failed."); 135 break; 136 case EXTRACT_INTERPRETER: 137 Log.e("Extracting interpreter failed."); 138 break; 139 case EXTRACT_INTERPRETER_EXTRAS: 140 Log.e("Extracting interpreter extras failed."); 141 break; 142 case EXTRACT_SCRIPTS: 143 Log.e("Extracting scripts failed."); 144 break; 145 } 146 Looper.myLooper().quit(); 147 } 148 }; 149 150 // TODO(Alexey): Add Javadoc. InterpreterInstaller(InterpreterDescriptor descriptor, Context context, AsyncTaskListener<Boolean> taskListener)151 public InterpreterInstaller(InterpreterDescriptor descriptor, Context context, 152 AsyncTaskListener<Boolean> taskListener) throws Sl4aException { 153 super(); 154 mDescriptor = descriptor; 155 mContext = context; 156 mTaskListener = taskListener; 157 mainThreadHandler = new Handler(); 158 mTaskQueue = new LinkedList<RequestCode>(); 159 160 String packageName = mDescriptor.getClass().getPackage().getName(); 161 162 if (packageName.length() == 0) { 163 throw new Sl4aException("Interpreter package name is empty."); 164 } 165 166 mInterpreterRoot = InterpreterConstants.SDCARD_ROOT + packageName; 167 168 if (mDescriptor == null) { 169 throw new Sl4aException("Interpreter description not provided."); 170 } 171 if (mDescriptor.getName() == null) { 172 throw new Sl4aException("Interpreter not specified."); 173 } 174 if (isInstalled()) { 175 throw new Sl4aException("Interpreter is installed."); 176 } 177 178 if (mDescriptor.hasInterpreterArchive()) { 179 mTaskQueue.offer(RequestCode.DOWNLOAD_INTERPRETER); 180 mTaskQueue.offer(RequestCode.EXTRACT_INTERPRETER); 181 } 182 if (mDescriptor.hasExtrasArchive()) { 183 mTaskQueue.offer(RequestCode.DOWNLOAD_INTERPRETER_EXTRAS); 184 mTaskQueue.offer(RequestCode.EXTRACT_INTERPRETER_EXTRAS); 185 } 186 if (mDescriptor.hasScriptsArchive()) { 187 mTaskQueue.offer(RequestCode.DOWNLOAD_SCRIPTS); 188 mTaskQueue.offer(RequestCode.EXTRACT_SCRIPTS); 189 } 190 } 191 192 @Override doInBackground(Void... params)193 protected Boolean doInBackground(Void... params) { 194 new Thread(new Runnable() { 195 @Override 196 public void run() { 197 executeInBackground(); 198 final boolean result = (mTaskQueue.size() == 0); 199 mainThreadHandler.post(new Runnable() { 200 @Override 201 public void run() { 202 finish(result); 203 } 204 }); 205 } 206 }).start(); 207 return true; 208 } 209 executeInBackground()210 private boolean executeInBackground() { 211 212 File root = new File(mInterpreterRoot); 213 if (root.exists()) { 214 FileUtils.delete(root); 215 } 216 if (!root.mkdirs()) { 217 Log.e("Failed to make directories: " + root.getAbsolutePath()); 218 return false; 219 } 220 221 if (Looper.myLooper() == null) { 222 Looper.prepare(); 223 } 224 mBackgroundHandler = new Handler(Looper.myLooper()); 225 mainThreadHandler.post(mTaskStarter); 226 Looper.loop(); 227 // Have we executed all the tasks? 228 return (mTaskQueue.size() == 0); 229 } 230 finish(boolean result)231 protected void finish(boolean result) { 232 if (result && setup()) { 233 mTaskListener.onTaskFinished(true, "Installation successful."); 234 } else { 235 if (mTaskHolder != null) { 236 mTaskHolder.cancel(true); 237 } 238 cleanup(); 239 mTaskListener.onTaskFinished(false, "Installation failed."); 240 } 241 } 242 download(String in)243 protected AsyncTask<Void, Integer, Long> download(String in) throws MalformedURLException { 244 String out = mInterpreterRoot; 245 return new UrlDownloaderTask(in, out, mContext); 246 } 247 downloadInterpreter()248 protected AsyncTask<Void, Integer, Long> downloadInterpreter() throws MalformedURLException { 249 return download(mDescriptor.getInterpreterArchiveUrl()); 250 } 251 downloadInterpreterExtras()252 protected AsyncTask<Void, Integer, Long> downloadInterpreterExtras() throws MalformedURLException { 253 return download(mDescriptor.getExtrasArchiveUrl()); 254 } 255 downloadScripts()256 protected AsyncTask<Void, Integer, Long> downloadScripts() throws MalformedURLException { 257 return download(mDescriptor.getScriptsArchiveUrl()); 258 } 259 extract(String in, String out, boolean replaceAll)260 protected AsyncTask<Void, Integer, Long> extract(String in, String out, boolean replaceAll) 261 throws Sl4aException { 262 return new ZipExtractorTask(in, out, mContext, replaceAll); 263 } 264 extractInterpreter()265 protected AsyncTask<Void, Integer, Long> extractInterpreter() throws Sl4aException { 266 String in = 267 new File(mInterpreterRoot, mDescriptor.getInterpreterArchiveName()).getAbsolutePath(); 268 String out = InterpreterUtils.getInterpreterRoot(mContext).getAbsolutePath(); 269 return extract(in, out, true); 270 } 271 extractInterpreterExtras()272 protected AsyncTask<Void, Integer, Long> extractInterpreterExtras() throws Sl4aException { 273 String in = new File(mInterpreterRoot, mDescriptor.getExtrasArchiveName()).getAbsolutePath(); 274 String out = mInterpreterRoot + InterpreterConstants.INTERPRETER_EXTRAS_ROOT; 275 return extract(in, out, true); 276 } 277 extractScripts()278 protected AsyncTask<Void, Integer, Long> extractScripts() throws Sl4aException { 279 String in = new File(mInterpreterRoot, mDescriptor.getScriptsArchiveName()).getAbsolutePath(); 280 String out = InterpreterConstants.SCRIPTS_ROOT; 281 return extract(in, out, false); 282 } 283 chmodIntepreter()284 protected boolean chmodIntepreter() { 285 int dataChmodErrno; 286 boolean interpreterChmodSuccess; 287 try { 288 dataChmodErrno = FileUtils.chmod(InterpreterUtils.getInterpreterRoot(mContext), 0755); 289 interpreterChmodSuccess = 290 FileUtils.recursiveChmod(InterpreterUtils.getInterpreterRoot(mContext, mDescriptor 291 .getName()), 0755); 292 } catch (Exception e) { 293 Log.e(e); 294 return false; 295 } 296 return dataChmodErrno == 0 && interpreterChmodSuccess; 297 } 298 isInstalled()299 protected boolean isInstalled() { 300 SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mContext); 301 return preferences.getBoolean(InterpreterConstants.INSTALLED_PREFERENCE_KEY, false); 302 } 303 cleanup()304 private void cleanup() { 305 List<File> directories = new ArrayList<File>(); 306 307 directories.add(new File(mInterpreterRoot)); 308 309 if (mDescriptor.hasInterpreterArchive()) { 310 if (!mTaskQueue.contains(RequestCode.EXTRACT_INTERPRETER)) { 311 directories.add(InterpreterUtils.getInterpreterRoot(mContext, mDescriptor.getName())); 312 } 313 } 314 315 for (File directory : directories) { 316 FileUtils.delete(directory); 317 } 318 } 319 setup()320 protected abstract boolean setup(); 321 } 322