001 /*
002 * ===========================================================================================
003 * = COPYRIGHT
004 *          PAX Computer Technology (Shenzhen) Co., Ltd. PROPRIETARY INFORMATION
005 *   This software is supplied under the terms of a license agreement or nondisclosure
006 *   agreement with PAX Computer Technology (Shenzhen) Co., Ltd. and may not be copied or
007 *   disclosed except in accordance with the terms in that agreement.
008 *     Copyright (C) 2017-2023 PAX Computer Technology (Shenzhen) Co., Ltd. All rights reserved.
009 * Description: // Detail description about the function of this module,
010 *             // interfaces with the other modules, and dependencies.
011 * Revision History:
012 * Date                         Author                        Action
013 * 2017/04/01                   PAX                     Create/Add/Modify/Delete
014 * ===========================================================================================
015 */
016
017package com.pax.neptunelite.api;
018
019import android.content.Context;
020import android.text.TextUtils;
021
022import com.pax.dal.IDAL;
023import com.pax.dal.proxy.IDALProxy;
024
025import java.io.BufferedInputStream;
026import java.io.File;
027import java.io.FileInputStream;
028import java.io.FileOutputStream;
029import java.io.IOException;
030import java.io.InputStream;
031import java.lang.reflect.Constructor;
032import java.lang.reflect.Field;
033import java.security.MessageDigest;
034import java.security.NoSuchAlgorithmException;
035import android.os.Build;
036import android.os.AsyncTask;
037import dalvik.system.DexClassLoader;
038import android.util.Log;
039
040public class NeptuneLiteUser {
041
042    private static NeptuneLiteUser neptuneLiteUser;
043    private IDAL dal;
044
045    private static final String DALPROXY_CLASS_NAME = "com.pax.dal.impl.DALProxy";
046    private static final String FILE_NAME = "nepcore.dex";
047
048    private static final String HASH256_OF_NEP_CORE_DEX = "d4bcc80ab13aac56bb16f0d85a3785262d4fa51fcefe4a75189511b36d683112";
049
050    private DexClassLoader dexClassLoader = null;
051
052    private NeptuneLiteUser() {
053
054    }
055    
056
057    public static synchronized NeptuneLiteUser getInstance() {
058        if (neptuneLiteUser == null) {
059            neptuneLiteUser = new NeptuneLiteUser();
060        }
061
062        return neptuneLiteUser;
063    }
064
065    /**
066     * <div class="en">get DAL interface</div> <div class="zh">获取DAL句柄</div>
067     *
068     * @param context
069     * @return
070     * @throws Exception
071     */
072    public synchronized IDAL getDal(Context context) throws Exception {
073        // NeptuneLiteProvider neptune = Utils.checkNeptuneLiteService(context);
074        // 如果已经加载过dex, 就直接返回IDAL接口
075        if (dal == null) {
076            dal = loadDalDex(context, FILE_NAME);
077        }
078        return dal;
079    }
080
081    /**
082     * <div class="zh">在多线程安全下获取的IDAL。</div> <div class="en">Get IDAL under multi-process security.</div>
083     * @param context context
084     * @return IDAL
085     * @throws Exception Exception
086     * @since V3.22.00
087     */
088    public synchronized IDAL getDalWithProcessSafe(Context context) throws Exception {
089        if (dal == null) {
090            int pid = android.os.Process.myPid();
091            String dexFileName = pid + ".dex";
092            dal = loadDalDex(context, dexFileName);
093            cleanRuntimeFiles(context);
094        }
095        return dal;
096    }
097
098    private void cleanRuntimeFiles(Context context) {
099        new AsyncTask<Void, Void, Void>() {
100            @Override
101            protected Void doInBackground(Void... voids) {
102                try {
103                    File targetDir = Build.VERSION.SDK_INT < Build.VERSION_CODES.O ?
104                            new File(context.getApplicationInfo().dataDir, "app_dex") :
105                            new File(context.getFilesDir(), "oat");
106                    cleanDex(targetDir);
107                } catch (Exception e) {
108                    Log.e("Neptune","CatchException: "+e);
109                }
110                return null;
111            }
112        }.execute();
113    }
114
115    private static void cleanDex(File dir) {
116        if (!dir.exists()) return;
117        File[] files = dir.listFiles();
118        if (files != null) {
119            for (File file : files) {
120                if (file.isDirectory()) {
121                    cleanDex(file);
122                } else {
123                    String name = file.getName();
124                    long fileTime = System.currentTimeMillis() - file.lastModified();
125                    if (name.matches("\\d+\\.(dex|vdex|odex|dex\\.cur\\.prof)") && (fileTime > (24 * 60 * 60 * 1000L))) {
126                        file.delete();
127                    }
128                }
129            }
130        }
131    }
132
133    /**
134     * 动态加载设备操作实现
135     *
136     * @param context
137     * @return
138     * @throws Exception
139     */
140    private IDAL loadDalDex(Context context, String dexFileName) throws Exception {
141        if (TextUtils.isEmpty(dexFileName)){
142            throw new Exception("LOAD DAL ERR");
143        }
144        File file = null;
145        try {
146            file = new File(context.getFilesDir().getAbsolutePath(), dexFileName);
147            if (!writeNepcoreFile(file)){
148                throw new Exception("LOAD DAL ERR");
149            }
150            file.setReadOnly();
151            // 动态加载
152            String dexPath = file.getAbsolutePath();
153            String hash = getFileHexStrHash(new File(dexPath));
154            if (hash != null && !hash.isEmpty()) {
155                if (!hash.equalsIgnoreCase(HASH256_OF_NEP_CORE_DEX)) {
156                    throw new Exception("DEX INTEGRITY CHECK ERR");
157                }
158            } else {
159                throw new Exception("GET FILE HASH ERR");
160            }
161
162            String optimizedDirectoryPath = context.getDir("dex", Context.MODE_PRIVATE).getAbsolutePath();
163            // String libraryPath = context.getFilesDir().getParent() + File.separator + "lib/";
164            // so路径
165            String libraryPath = context.getApplicationInfo().nativeLibraryDir;
166            dexClassLoader = new DexClassLoader(dexPath, optimizedDirectoryPath, libraryPath, context.getClassLoader());
167            Class<?> glClass = dexClassLoader.loadClass(DALPROXY_CLASS_NAME);
168            Constructor<?> istructor = glClass.getConstructor(Context.class);
169            IDALProxy dalProxy = ((IDALProxy) istructor.newInstance(context));
170            return dalProxy.getDal();
171        } catch (Exception e) {
172            Log.e("Neptune","CatchException: "+e);
173            throw new Exception("LOAD DAL ERR");
174        } finally {
175            // 删除临时文件
176            if (null != file && file.exists()) {
177                file.delete();
178            }
179        }
180    }
181
182    private String getNepcoreFieldValue(Nepcore nepcore, String fieldName) {
183        Class<?> clazz = nepcore.getClass();
184        Field[] fields = clazz.getDeclaredFields();
185        for (Field field : fields) {
186            if (fieldName.equals(field.getName())) {
187                field.setAccessible(true);
188                try {
189                    return (String) field.get(nepcore);
190                } catch (IllegalAccessException | IllegalArgumentException e) {
191                    Log.e("Neptune","CatchException: "+e);
192                }
193            }
194        }
195        return null;
196    }
197
198    private boolean writeNepcoreFile(File file) {
199        FileOutputStream out = null;
200        try {
201            // 如果文件存在,则先删除
202            if (file == null) {
203                return false;
204            }
205            if (file.exists()) {
206                file.delete();
207            }
208            out = new FileOutputStream(file);
209            Nepcore nepcore = new Nepcore();
210            for (int i = 0; i < nepcore.count; i++) {
211                String dexData = getNepcoreFieldValue(nepcore, "nepcore" + i);
212                if (dexData != null) {
213                    byte[] dex = strToBcd(dexData);
214                    out.write(dex, 0, dex.length);
215                }
216            }
217            return true;
218        } catch (IOException e) {
219            Log.e("Neptune","CatchException: "+e);
220            return false;
221        } finally {
222            try {
223                if (out != null) {
224                    out.flush();
225                }
226            } catch (IOException e) {
227                Log.e("Neptune","CatchException: "+e);
228            }
229
230            try {
231                if (out != null) {
232                    out.close();
233                }
234            } catch (IOException e) {
235                Log.e("Neptune","CatchException: "+e);
236            }
237        }
238    }
239
240    byte[] strToBcd(String str) {
241        int len = str.length();
242        int mod = len % 2;
243        if (mod != 0) {
244            str = str + "0";
245            len = str.length();
246        }
247        byte[] abt;
248        if (len >= 2) {
249            len = len / 2;
250        }
251        byte[] bbt = new byte[len];
252        abt = str.getBytes();
253        int j, k;
254        for (int p = 0; p < str.length() / 2; p++) {
255            if ((abt[2 * p] >= 'a') && (abt[2 * p] <= 'z')) {
256                j = abt[2 * p] - 'a' + 0x0a;
257            } else if ((abt[2 * p] >= 'A') && (abt[2 * p] <= 'Z')) {
258                j = abt[2 * p] - 'A' + 0x0a;
259            } else {
260                j = abt[2 * p] - '0';
261            }
262
263            if ((abt[2 * p + 1] >= 'a') && (abt[2 * p + 1] <= 'z')) {
264                k = abt[2 * p + 1] - 'a' + 0x0a;
265            } else if ((abt[2 * p + 1] >= 'A') && (abt[2 * p + 1] <= 'Z')) {
266                k = abt[2 * p + 1] - 'A' + 0x0a;
267            } else {
268                k = abt[2 * p + 1] - '0';
269            }
270
271            int a = (j << 4) + k;
272            byte b = (byte) a;
273            bbt[p] = b;
274        }
275        return bbt;
276    }
277
278
279    private static String getFileHexStrHash(File file) {
280        return byteArrayToHexStr(getFileByteHash(file));
281    }
282
283    private static byte[] getFileByteHash(File file) {
284        if (file == null || !file.exists()) {
285            return null;
286        }
287        InputStream inputStream = null;
288        try {
289            MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
290            inputStream = new BufferedInputStream(new FileInputStream(file));
291            byte[] temp = new byte[2048];
292            int len;
293            while ((len = inputStream.read(temp)) != -1) {
294                messageDigest.update(temp, 0, len);
295            }
296            return messageDigest.digest();
297        } catch (IOException | NoSuchAlgorithmException e) {
298            Log.e("Neptune","CatchException: "+e);
299            return null;
300        } finally {
301            if (inputStream != null) {
302                try {
303                    inputStream.close();
304                } catch (IOException e) {
305                    Log.e("Neptune","CatchException: "+e);
306                }
307            }
308        }
309    }
310
311    private static String byteArrayToHexStr(byte[] byteArray) {
312        if (byteArray == null) {
313            return null;
314        }
315        char[] hexArray = "0123456789ABCDEF".toCharArray();
316        char[] hexChars = new char[byteArray.length * 2];
317        for (int j = 0; j < byteArray.length; j++) {
318            int v = byteArray[j] & 0xFF;
319            hexChars[j * 2] = hexArray[v >>> 4];
320            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
321        }
322        return new String(hexChars);
323    }
324
325}