/* * Tencent is pleased to support the open source community by making Tinker available. * * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * * https://opensource.org/licenses/BSD-3-Clause * * Unless required by applicable law or agreed to in writing, software distributed under the License is * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied. See the License for the specific language governing permissions and * limitations under the License. */ package com.tencent.tinker.loader.shareutil; import android.os.Build; import android.util.Log; import com.tencent.tinker.loader.TinkerRuntimeException; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.Properties; /** * Created by zhangshaowen on 16/3/16. */ public class SharePatchInfo { public static final int MAX_EXTRACT_ATTEMPTS = ShareConstants.MAX_EXTRACT_ATTEMPTS; public static final String OLD_VERSION = ShareConstants.OLD_VERSION; public static final String NEW_VERSION = ShareConstants.NEW_VERSION; public static final String FINGER_PRINT = "print"; public static final String OAT_DIR = "dir"; public static final String DEFAULT_DIR = ShareConstants.DEFAULT_DEX_OPTIMIZE_PATH; private static final String TAG = "PatchInfo"; public String oldVersion; public String newVersion; public String fingerPrint; public String oatDir; public SharePatchInfo(String oldVer, String newVew, String finger, String oatDir) { // TODO Auto-generated constructor stub this.oldVersion = oldVer; this.newVersion = newVew; this.fingerPrint = finger; this.oatDir = oatDir; } public static SharePatchInfo readAndCheckPropertyWithLock(File pathInfoFile, File lockFile) { if (pathInfoFile == null || lockFile == null) { return null; } File lockParentFile = lockFile.getParentFile(); if (!lockParentFile.exists()) { lockParentFile.mkdirs(); } SharePatchInfo patchInfo; ShareFileLockHelper fileLock = null; try { fileLock = ShareFileLockHelper.getFileLock(lockFile); patchInfo = readAndCheckProperty(pathInfoFile); } catch (Exception e) { throw new TinkerRuntimeException("readAndCheckPropertyWithLock fail", e); } finally { try { if (fileLock != null) { fileLock.close(); } } catch (IOException e) { Log.i(TAG, "releaseInfoLock error", e); } } return patchInfo; } public static boolean rewritePatchInfoFileWithLock(File pathInfoFile, SharePatchInfo info, File lockFile) { if (pathInfoFile == null || info == null || lockFile == null) { return false; } File lockParentFile = lockFile.getParentFile(); if (!lockParentFile.exists()) { lockParentFile.mkdirs(); } boolean rewriteSuccess; ShareFileLockHelper fileLock = null; try { fileLock = ShareFileLockHelper.getFileLock(lockFile); rewriteSuccess = rewritePatchInfoFile(pathInfoFile, info); } catch (Exception e) { throw new TinkerRuntimeException("rewritePatchInfoFileWithLock fail", e); } finally { try { if (fileLock != null) { fileLock.close(); } } catch (IOException e) { Log.i(TAG, "releaseInfoLock error", e); } } return rewriteSuccess; } private static SharePatchInfo readAndCheckProperty(File pathInfoFile) { boolean isReadPatchSuccessful = false; int numAttempts = 0; String oldVer = null; String newVer = null; String lastFingerPrint = null; String oatDIr = null; while (numAttempts < MAX_EXTRACT_ATTEMPTS && !isReadPatchSuccessful) { numAttempts++; Properties properties = new Properties(); FileInputStream inputStream = null; try { inputStream = new FileInputStream(pathInfoFile); properties.load(inputStream); oldVer = properties.getProperty(OLD_VERSION); newVer = properties.getProperty(NEW_VERSION); lastFingerPrint = properties.getProperty(FINGER_PRINT); oatDIr = properties.getProperty(OAT_DIR); } catch (IOException e) { // e.printStackTrace(); Log.e(TAG, "read property failed, e:" + e); } finally { SharePatchFileUtil.closeQuietly(inputStream); } if (oldVer == null || newVer == null) { continue; } //oldVer may be "" or 32 md5 if ((!oldVer.equals("") && !SharePatchFileUtil.checkIfMd5Valid(oldVer)) || !SharePatchFileUtil.checkIfMd5Valid(newVer)) { Log.w(TAG, "path info file corrupted:" + pathInfoFile.getAbsolutePath()); continue; } else { isReadPatchSuccessful = true; } } if (isReadPatchSuccessful) { return new SharePatchInfo(oldVer, newVer, lastFingerPrint, oatDIr); } return null; } private static boolean rewritePatchInfoFile(File pathInfoFile, SharePatchInfo info) { if (pathInfoFile == null || info == null) { return false; } // write fingerprint if it is null or nil if (ShareTinkerInternals.isNullOrNil(info.fingerPrint)) { info.fingerPrint = Build.FINGERPRINT; } if (ShareTinkerInternals.isNullOrNil(info.oatDir)) { info.oatDir = DEFAULT_DIR; } Log.i(TAG, "rewritePatchInfoFile file path:" + pathInfoFile.getAbsolutePath() + " , oldVer:" + info.oldVersion + ", newVer:" + info.newVersion + ", fingerprint:" + info.fingerPrint + ", oatDir:" + info.oatDir); boolean isWritePatchSuccessful = false; int numAttempts = 0; File parentFile = pathInfoFile.getParentFile(); if (!parentFile.exists()) { parentFile.mkdirs(); } while (numAttempts < MAX_EXTRACT_ATTEMPTS && !isWritePatchSuccessful) { numAttempts++; Properties newProperties = new Properties(); newProperties.put(OLD_VERSION, info.oldVersion); newProperties.put(NEW_VERSION, info.newVersion); newProperties.put(FINGER_PRINT, info.fingerPrint); newProperties.put(OAT_DIR, info.oatDir); FileOutputStream outputStream = null; try { outputStream = new FileOutputStream(pathInfoFile, false); String comment = "from old version:" + info.oldVersion + " to new version:" + info.newVersion; newProperties.store(outputStream, comment); } catch (Exception e) { e.printStackTrace(); } finally { SharePatchFileUtil.closeQuietly(outputStream); } SharePatchInfo tempInfo = readAndCheckProperty(pathInfoFile); isWritePatchSuccessful = tempInfo != null && tempInfo.oldVersion.equals(info.oldVersion) && tempInfo.newVersion.equals(info.newVersion); if (!isWritePatchSuccessful) { pathInfoFile.delete(); } } if (isWritePatchSuccessful) { return true; } return false; } }