Home | History | Annotate | Download | only in android_scripting
      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.
    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
    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 
    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 
    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 
    243   protected AsyncTask<Void, Integer, Long> download(String in) throws MalformedURLException {
    244     String out = mInterpreterRoot;
    245     return new UrlDownloaderTask(in, out, mContext);
    246   }
    247 
    248   protected AsyncTask<Void, Integer, Long> downloadInterpreter() throws MalformedURLException {
    249     return download(mDescriptor.getInterpreterArchiveUrl());
    250   }
    251 
    252   protected AsyncTask<Void, Integer, Long> downloadInterpreterExtras() throws MalformedURLException {
    253     return download(mDescriptor.getExtrasArchiveUrl());
    254   }
    255 
    256   protected AsyncTask<Void, Integer, Long> downloadScripts() throws MalformedURLException {
    257     return download(mDescriptor.getScriptsArchiveUrl());
    258   }
    259 
    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 
    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 
    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 
    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 
    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 
    299   protected boolean isInstalled() {
    300     SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mContext);
    301     return preferences.getBoolean(InterpreterConstants.INSTALLED_PREFERENCE_KEY, false);
    302   }
    303 
    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 
    320   protected abstract boolean setup();
    321 }
    322