Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 87fd250c authored by mqi's avatar mqi Committed by Steve Kondik
Browse files

Improve the scan process

Use multi task to speed up the scan process

CRs-Fixed: 984513

base: fix native crash in system_server

Currently PackageManagerService uses multi-thread to scan
packages to speed up, when scan each package, it sometimes
will call SELinux's restorecon, then libselinux uses global
variable fc_sehandle to selabel_lookup and write in compile_regex,
so it's not thread-safe, so will cause invalid address with
possibility. From backtrace, the final crash happens in pcre_exec.

Add one lock in NativeLibraryHelper to make restorecon safe.

Change-Id: Ida43fcda01d3450befea6afa0be5da27bb195def
CRs-Fixed: 1002406, 1027381
parent 75c84499
Loading
Loading
Loading
Loading
+8 −2
Original line number Diff line number Diff line
@@ -62,6 +62,8 @@ public class NativeLibraryHelper {
    // that the cpuAbiOverride must be clear.
    public static final String CLEAR_ABI_OVERRIDE = "-";

    private static final Object mRestoreconSync = new Object();

    /**
     * A handle to an opened package, consisting of one or more APKs. Used as
     * input to the various NativeLibraryHelper methods. Allows us to scan and
@@ -275,10 +277,14 @@ public class NativeLibraryHelper {
                throw new IOException("Cannot chmod native library directory "
                        + path.getPath(), e);
            }
        } else if (!SELinux.restorecon(path)) {
        } else {
            synchronized (mRestoreconSync) {
                if (!SELinux.restorecon(path)) {
                    throw new IOException("Cannot set SELinux context for " + path.getPath());
                }
            }
        }
    }

    private static long sumNativeBinariesForSupportedAbi(Handle handle, String[] abiList) {
        int abi = findSupportedAbi(handle, abiList);
+141 −0
Original line number Diff line number Diff line
/*
* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*    * Redistributions of source code must retain the above copyright
*      notice, this list of conditions and the following disclaimer.
*    * Redistributions in binary form must reproduce the above
*      copyright notice, this list of conditions and the following
*      disclaimer in the documentation and/or other materials provided
*      with the distribution.
*    * Neither the name of The Linux Foundation nor the names of its
*      contributors may be used to endorse or promote products derived
*      from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.android.server.pm;

import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;

import android.util.Log;

public class MultiTaskDealer {

    public static final String TAG = "MultiTaskDealer";
    public static final String PACKAGEMANAGER_SCANER = "packagescan";
    private static final boolean DEBUG_TASK = false;

    private static HashMap<String, WeakReference<MultiTaskDealer>> map = new HashMap<String, WeakReference<MultiTaskDealer>>();

    public static MultiTaskDealer getDealer(String name) {
        WeakReference<MultiTaskDealer> ref = map.get(name);
        MultiTaskDealer dealer = ref!=null?ref.get():null;
        return dealer;
    }

    public static MultiTaskDealer startDealer(String name,int taskCount) {
        MultiTaskDealer dealer = getDealer(name);
        if(dealer==null) {
            dealer = new MultiTaskDealer(name,taskCount);
            WeakReference<MultiTaskDealer> ref = new WeakReference<MultiTaskDealer>(dealer);
            map.put(name,ref);
        }
        return dealer;
    }

    public void startLock() {
        mLock.lock();
    }

    public void endLock() {
        mLock.unlock();
    }

    private ThreadPoolExecutor mExecutor;
    private int mTaskCount = 0;
    private boolean mNeedNotifyEnd = false;
    private Object mObjWaitAll = new Object();
    private ReentrantLock mLock = new ReentrantLock();

    public MultiTaskDealer(String name,int taskCount) {
        final String taskName = name;
        ThreadFactory factory = new ThreadFactory()
        {
            private final AtomicInteger mCount = new AtomicInteger(1);

            public Thread newThread(final Runnable r) {
                if (DEBUG_TASK) Log.d(TAG, "create a new thread:" + taskName);
                return new Thread(r, taskName + "-" + mCount.getAndIncrement());
            }
        };
        mExecutor = new ThreadPoolExecutor(taskCount, taskCount, 5, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(), factory){
            protected void afterExecute(Runnable r, Throwable t) {
                if(t!=null) {
                    t.printStackTrace();
                }
                MultiTaskDealer.this.TaskCompleteNotify(r);
                if (DEBUG_TASK) Log.d(TAG, "end task");
                super.afterExecute(r,t);
            }
            protected void beforeExecute(Thread t, Runnable r) {
                if (DEBUG_TASK) Log.d(TAG, "start task");
                super.beforeExecute(t,r);
            }
        };
    }

    public void addTask(Runnable task) {
        synchronized (mObjWaitAll) {
            mTaskCount+=1;
        }
        mExecutor.execute(task);
        if (DEBUG_TASK) Log.d(TAG, "addTask");
    }

    private void TaskCompleteNotify(Runnable task) {
        synchronized (mObjWaitAll) {
            mTaskCount-=1;
            if(mTaskCount<=0 && mNeedNotifyEnd) {
                if (DEBUG_TASK) Log.d(TAG, "complete notify");
                mObjWaitAll.notify();
            }
        }
    }

    public void waitAll() {
        if (DEBUG_TASK) Log.d(TAG, "start wait all");
        synchronized (mObjWaitAll) {
            if(mTaskCount>0) {
                mNeedNotifyEnd = true;
                try {
                    mObjWaitAll.wait();
                } catch (Exception e) {
                }
                mNeedNotifyEnd = false;
            }
            if (DEBUG_TASK) Log.d(TAG, "wait finish");
            return;
        }
    }
}
+5 −1
Original line number Diff line number Diff line
@@ -57,6 +57,8 @@ final class PackageDexOptimizer {
    private final PowerManager.WakeLock mDexoptWakeLock;
    private volatile boolean mSystemReady;

    private Object mDeferredDexOptSync = new Object();

    PackageDexOptimizer(PackageManagerService packageManagerService) {
        this.mPackageManagerService = packageManagerService;
        PowerManager powerManager = (PowerManager)packageManagerService.mContext.getSystemService(
@@ -150,7 +152,9 @@ final class PackageDexOptimizer {
                    // We're deciding to defer a needed dexopt. Don't bother dexopting for other
                    // paths and instruction sets. We'll deal with them all together when we process
                    // our list of deferred dexopts.
                    synchronized (mDeferredDexOptSync) {
                        addPackageForDeferredDexopt(pkg);
                    }
                    return DEX_OPT_DEFERRED;
                }

+71 −41
Original line number Diff line number Diff line
/*
 * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
 * Not a Contribution.
 *
 * Copyright (C) 2006 The Android Open Source Project
 * This code has been modified.  Portions copyright (C) 2010, T-Mobile USA, Inc.
 *
@@ -1974,9 +1977,9 @@ public class PackageManagerService extends IPackageManager.Stub {
        mAvailableFeatures = systemConfig.getAvailableFeatures();
        mSignatureAllowances = systemConfig.getSignatureAllowances();
        synchronized (mInstallLock) {
//        synchronized (mInstallLock) {
        // writer
        synchronized (mPackages) {
//        synchronized (mPackages) {
            mHandlerThread = new ServiceThread(TAG,
                    Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
            mHandlerThread.start();
@@ -2500,8 +2503,8 @@ public class PackageManagerService extends IPackageManager.Stub {
            mIntentFilterVerifier = new IntentVerifierProxy(mContext,
                    mIntentFilterVerifierComponent);
        } // synchronized (mPackages)
        } // synchronized (mInstallLock)
        //} // synchronized (mPackages)
        //} // synchronized (mInstallLock)
        // Now after opening every single application zip, make sure they
        // are all flushed.  Not really needed, but keeps things nice and
@@ -5827,7 +5830,7 @@ public class PackageManagerService extends IPackageManager.Stub {
    }
    private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime,
            UserHandle user) {
            final UserHandle user) {
        final File[] files = dir.listFiles();
        if (ArrayUtils.isEmpty(files)) {
            Log.d(TAG, "No files in app dir " + dir);
@@ -5839,8 +5842,8 @@ public class PackageManagerService extends IPackageManager.Stub {
                    + " flags=0x" + Integer.toHexString(parseFlags));
        }
        int prebundledUserId = user == null ? UserHandle.USER_OWNER : user.getIdentifier();
        boolean isPrebundled = (parseFlags & PackageParser.PARSE_IS_PREBUNDLED_DIR) != 0;
        final int prebundledUserId = user == null ? UserHandle.USER_OWNER : user.getIdentifier();
        final boolean isPrebundled = (parseFlags & PackageParser.PARSE_IS_PREBUNDLED_DIR) != 0;
        if (isPrebundled) {
            synchronized (mPackages) {
                if (DEBUG_PREBUNDLED_SCAN) Log.d(TAG, "Reading prebundled packages for user "
@@ -5849,6 +5852,13 @@ public class PackageManagerService extends IPackageManager.Stub {
            }
        }
        Log.d(TAG, "start scanDirLI:"+dir);
        // use multi thread to speed up scanning
        int iMultitaskNum = SystemProperties.getInt("persist.pm.multitask", 6);
        Log.d(TAG, "max thread:" + iMultitaskNum);
        final MultiTaskDealer dealer = (iMultitaskNum > 1) ? MultiTaskDealer.startDealer(
                MultiTaskDealer.PACKAGEMANAGER_SCANER, iMultitaskNum) : null;
        for (File file : files) {
            final boolean isPackage = (isApkFile(file) || file.isDirectory())
                    && !PackageInstallerService.isStageName(file.getName());
@@ -5856,17 +5866,25 @@ public class PackageManagerService extends IPackageManager.Stub {
                // Ignore entries which are not packages
                continue;
            }
            final File ref_file = file;
            final int ref_parseFlags = parseFlags;
            final int ref_scanFlags = scanFlags;
            final long ref_currentTime = currentTime;
            Runnable scanTask = new Runnable() {
                public void run() {
                    try {
                scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
                        scanFlags, currentTime, user);
                        scanPackageLI(ref_file, ref_parseFlags | PackageParser.PARSE_MUST_BE_APK,
                                ref_scanFlags, ref_currentTime, user);
                        if (isPrebundled) {
                            final PackageParser.Package pkg;
                            try {
                        pkg = new PackageParser().parsePackage(file, parseFlags);
                                pkg = new PackageParser().parsePackage(ref_file, ref_parseFlags);
                            } catch (PackageParserException e) {
                                throw PackageManagerException.from(e);
                            }
                    synchronized (mPackages) {
                            if (DEBUG_PREBUNDLED_SCAN) Log.d(TAG,
                                    "Marking prebundled package " + pkg.packageName +
                                            " for user " + prebundledUserId);
@@ -5881,21 +5899,31 @@ public class PackageManagerService extends IPackageManager.Stub {
                                        pkg.packageName);
                            }
                        }
                }
                    } catch (PackageManagerException e) {
                Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());
                        Slog.w(TAG, "Failed to parse " + ref_file + ": " + e.getMessage());
                        // Delete invalid userdata apps
                if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
                        if ((ref_parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
                                e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {
                    logCriticalInfo(Log.WARN, "Deleting invalid package at " + file);
                    if (file.isDirectory()) {
                        mInstaller.rmPackageDir(file.getAbsolutePath());
                            logCriticalInfo(Log.WARN, "Deleting invalid package at " + ref_file);
                            if (ref_file.isDirectory()) {
                                mInstaller.rmPackageDir(ref_file.getAbsolutePath());
                            } else {
                        file.delete();
                                ref_file.delete();
                            }
                        }
                    }
                }
            };
            if (dealer != null)
                dealer.addTask(scanTask);
            else
                scanTask.run();
        }
        if (dealer != null) {
            dealer.waitAll();
        }
        if (isPrebundled) {
@@ -5905,6 +5933,8 @@ public class PackageManagerService extends IPackageManager.Stub {
                mSettings.writePrebundledPackagesLPr(prebundledUserId);
            }
        }
        Log.d(TAG, "end scanDirLI:"+dir);
    }
    private static File getSettingsProblemFile() {
@@ -5918,7 +5948,7 @@ public class PackageManagerService extends IPackageManager.Stub {
        logCriticalInfo(priority, msg);
    }
    static void logCriticalInfo(int priority, String msg) {
    static synchronized void logCriticalInfo(int priority, String msg) {
        Slog.println(priority, TAG, msg);
        EventLogTags.writePmCriticalInfo(msg);
        try {