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

Commit 402d78eb authored by Jorim Jaggi's avatar Jorim Jaggi
Browse files

Pin compiled code of HOME app

Keeping the code in memory of the currently set home app is
important for latency as we don't have any kind of starting
window/splash screen when pressing the home app to hide any latency.

Memory impact:

Pinning dex/vdex:

In practical scenarios, this should be < 500kb.
The home app is usually profile-speed compiled, for which the
resulting dex/vdex files are about 2 mb. However, during regular
use, at least 1.5 MB of it is referenced in memory. This makes
sense: By definition profile-speed only compiles the things that
is usually frequently executed during regular execution.

Pinning apk:
With Launcher 3 in practical scenarios this should be about 3.7 MB,
as the APK is about 5.7 MB but 2 MB are usually referenced in any
case.

Bug: 111132016
Bug: 78585335
Test: Inspect "adb shell dumpsys pinner" after boot.
Test: Check for pinned files after updating camera/home.
Test: Check for pinned files after user switch with different
default apps.
Test: Check for pinned files after bg-dexopt.
Test: Check for pinned files after bg-dexopt + kill pid.

Change-Id: I6cdbc06d089efeb1c72a51216879ba0573502009
parent ca8a8a99
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -223,6 +223,12 @@ public abstract class ActivityManagerInternal {
     * intercept activity launches for work apps when the Work Challenge is present.
     */
    public abstract boolean shouldConfirmCredentials(int userId);

    /**
     * @return The intent used to launch the home activity.
     */
    public abstract Intent getHomeIntent();

    public abstract int[] getCurrentProfileIds();
    public abstract UserInfo getCurrentUser();
    public abstract void ensureNotSpecialUser(int userId);
+3 −0
Original line number Diff line number Diff line
@@ -3182,6 +3182,9 @@
    <!-- True if camera app should be pinned via Pinner Service -->
    <bool name="config_pinnerCameraApp">false</bool>

    <!-- True if home app should be pinned via Pinner Service -->
    <bool name="config_pinnerHomeApp">false</bool>

    <!-- Number of days preloaded file cache should be preserved on a device before it can be
         deleted -->
    <integer name="config_keepPreloadsMinDays">7</integer>
+1 −0
Original line number Diff line number Diff line
@@ -2922,6 +2922,7 @@
  <!-- Pinner Service -->
  <java-symbol type="array" name="config_defaultPinnerServiceFiles" />
  <java-symbol type="bool" name="config_pinnerCameraApp" />
  <java-symbol type="bool" name="config_pinnerHomeApp" />

  <java-symbol type="string" name="config_doubleTouchGestureEnableFile" />

+353 −105
Original line number Diff line number Diff line
@@ -16,8 +16,16 @@

package com.android.server;

import static android.app.ActivityManager.UID_OBSERVER_ACTIVE;
import static android.app.ActivityManager.UID_OBSERVER_GONE;

import android.annotation.IntDef;
import android.annotation.Nullable;

import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.IActivityManager;
import android.app.IUidObserver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -32,18 +40,21 @@ import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.MediaStore;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import android.system.StructStat;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.ResolverActivity;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.function.pooled.PooledLambda;

import dalvik.system.DexFile;
import dalvik.system.VMRuntime;
@@ -53,12 +64,12 @@ import java.io.Closeable;
import java.io.InputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.EOFException;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;

import java.util.zip.ZipFile;
import java.util.zip.ZipException;
import java.util.zip.ZipEntry;

/**
@@ -70,16 +81,50 @@ import java.util.zip.ZipEntry;
public final class PinnerService extends SystemService {
    private static final boolean DEBUG = false;
    private static final String TAG = "PinnerService";
    private static final int MAX_CAMERA_PIN_SIZE = 80 * (1 << 20); //80MB max

    private static final String PIN_META_FILENAME = "pinlist.meta";
    private static final int PAGE_SIZE = (int) Os.sysconf(OsConstants._SC_PAGESIZE);
    private static final int MATCH_FLAGS = PackageManager.MATCH_DEFAULT_ONLY
            | PackageManager.MATCH_DIRECT_BOOT_AWARE
            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;

    private static final int KEY_CAMERA = 0;
    private static final int KEY_HOME = 1;

    private static final int MAX_CAMERA_PIN_SIZE = 80 * (1 << 20); // 80MB max for camera app.
    private static final int MAX_HOME_PIN_SIZE = 6 * (1 << 20); // 6MB max for home app.

    @IntDef({KEY_CAMERA, KEY_HOME})
    @Retention(RetentionPolicy.SOURCE)
    public @interface AppKey {}

    private final Context mContext;
    private final boolean mShouldPinCamera;
    private final ActivityManagerInternal mAmInternal;
    private final IActivityManager mAm;

    /* These lists protected by PinnerService monitor lock */
    private final ArrayList<PinnedFile> mPinnedFiles = new ArrayList<PinnedFile>();
    private final ArrayList<PinnedFile> mPinnedCameraFiles = new ArrayList<PinnedFile>();
    /** The list of the statically pinned files. */
    @GuardedBy("this")
    private final ArrayList<PinnedFile> mPinnedFiles = new ArrayList<>();

    /** The list of the pinned apps. This is a map from {@link AppKey} to a pinned app. */
    @GuardedBy("this")
    private final ArrayMap<Integer, PinnedApp> mPinnedApps = new ArrayMap<>();

    /**
     * The list of the pinned apps that need to be repinned as soon as the all processes of a given
     * uid are no longer active. Note that with background dex opt, the new dex/vdex files are only
     * loaded into the processes once it restarts. So in case background dex opt recompiled these
     * files, we still need to keep the old ones pinned until the processes restart.
     * <p>
     * This is a map from uid to {@link AppKey}
     */
    @GuardedBy("this")
    private final ArrayMap<Integer, Integer> mPendingRepin = new ArrayMap<>();

    /**
     * A set of {@link AppKey} that are configured to be pinned.
     */
    private final ArraySet<Integer> mPinKeys = new ArraySet<>();

    private BinderService mBinderService;
    private PinnerHandler mPinnerHandler = null;
@@ -87,13 +132,13 @@ public final class PinnerService extends SystemService {
    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
          // If this user's camera app has been updated, update pinned files accordingly.
          if (intent.getAction() == Intent.ACTION_PACKAGE_REPLACED) {
          // If an app has updated, update pinned files accordingly.
          if (Intent.ACTION_PACKAGE_REPLACED.equals(intent.getAction())) {
                Uri packageUri = intent.getData();
                String packageName = packageUri.getSchemeSpecificPart();
                ArraySet<String> updatedPackages = new ArraySet<>();
                updatedPackages.add(packageName);
                update(updatedPackages);
                update(updatedPackages, true /* force */);
            }
        }
    };
@@ -102,14 +147,26 @@ public final class PinnerService extends SystemService {
        super(context);

        mContext = context;
        mShouldPinCamera = context.getResources().getBoolean(
        boolean shouldPinCamera = context.getResources().getBoolean(
                com.android.internal.R.bool.config_pinnerCameraApp);
        boolean shouldPinHome = context.getResources().getBoolean(
                com.android.internal.R.bool.config_pinnerHomeApp);
        if (shouldPinCamera) {
            mPinKeys.add(KEY_CAMERA);
        }
        if (shouldPinHome) {
            mPinKeys.add(KEY_HOME);
        }
        mPinnerHandler = new PinnerHandler(BackgroundThread.get().getLooper());

        mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
        mAm = ActivityManager.getService();

        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
        filter.addDataScheme("package");
        mContext.registerReceiver(mBroadcastReceiver, filter);
        registerUidListener();
    }

    @Override
@@ -122,32 +179,39 @@ public final class PinnerService extends SystemService {
        publishLocalService(PinnerService.class, this);

        mPinnerHandler.obtainMessage(PinnerHandler.PIN_ONSTART_MSG).sendToTarget();
        mPinnerHandler.obtainMessage(PinnerHandler.PIN_CAMERA_MSG, UserHandle.USER_SYSTEM, 0)
                .sendToTarget();
        sendPinAppsMessage(UserHandle.USER_SYSTEM);
    }

    /**
     * Pin camera on user switch.
     * If more than one user is using the device
     * each user may set a different preference for the camera app.
     * Make sure that user's preference is pinned into memory.
     * Repin apps on user switch.
     * <p>
     * If more than one user is using the device each user may set a different preference for the
     * individual apps. Make sure that user's preference is pinned into memory.
     */
    @Override
    public void onSwitchUser(int userHandle) {
        mPinnerHandler.obtainMessage(PinnerHandler.PIN_CAMERA_MSG, userHandle, 0).sendToTarget();
        sendPinAppsMessage(userHandle);
    }

    @Override
    public void onUnlockUser(int userHandle) {
        sendPinAppsMessage(userHandle);
    }

    /**
     * Update the currently pinned files.
     * Specifically, this only updates camera pinning.
     * Specifically, this only updates pinning for the apps that need to be pinned.
     * The other files pinned in onStart will not need to be updated.
     */
    public void update(ArraySet<String> updatedPackages) {
        ApplicationInfo cameraInfo = getCameraInfo(UserHandle.USER_SYSTEM);
        if (cameraInfo != null && updatedPackages.contains(cameraInfo.packageName)) {
            Slog.i(TAG, "Updating pinned files.");
            mPinnerHandler.obtainMessage(PinnerHandler.PIN_CAMERA_MSG, UserHandle.USER_SYSTEM, 0)
                    .sendToTarget();
    public void update(ArraySet<String> updatedPackages, boolean force) {
        int currentUser = ActivityManager.getCurrentUser();
        for (int i = mPinKeys.size() - 1; i >= 0; i--) {
            int key = mPinKeys.valueAt(i);
            ApplicationInfo info = getInfoForKey(key, currentUser);
            if (info != null && updatedPackages.contains(info.packageName)) {
                Slog.i(TAG, "Updating pinned files for " + info.packageName + " force=" + force);
                sendPinAppMessage(key, currentUser, force);
            }
        }
    }

@@ -174,25 +238,80 @@ public final class PinnerService extends SystemService {
        }
    }

    /**
     * Handler for camera pinning message
     */
    private void handlePinCamera(int userHandle) {
        if (!mShouldPinCamera) return;
        if (!pinCamera(userHandle)) {
            if (DEBUG) {
                Slog.v(TAG, "Failed to pin camera.");
    private void registerUidListener() {
        try {
            mAm.registerUidObserver(new IUidObserver.Stub() {
                @Override
                public void onUidGone(int uid, boolean disabled) throws RemoteException {
                    mPinnerHandler.sendMessage(PooledLambda.obtainMessage(
                            PinnerService::handleUidGone, PinnerService.this, uid));
                }

                @Override
                public void onUidActive(int uid) throws RemoteException {
                    mPinnerHandler.sendMessage(PooledLambda.obtainMessage(
                            PinnerService::handleUidActive, PinnerService.this, uid));
                }

                @Override
                public void onUidIdle(int uid, boolean disabled) throws RemoteException {
                }

                @Override
                public void onUidStateChanged(int uid, int procState, long procStateSeq)
                        throws RemoteException {
                }

                @Override
                public void onUidCachedChanged(int uid, boolean cached) throws RemoteException {
                }
            }, UID_OBSERVER_GONE | UID_OBSERVER_ACTIVE, 0, "system");
        } catch (RemoteException e) {
            Slog.e(TAG, "Failed to register uid observer", e);
        }
    }

    private void handleUidGone(int uid) {
        updateActiveState(uid, false /* active */);
        int key;
        synchronized (this) {

            // In case we have a pending repin, repin now. See mPendingRepin for more information.
            key = mPendingRepin.getOrDefault(uid, -1);
            if (key == -1) {
                return;
            }
            mPendingRepin.remove(uid);
        }
        pinApp(key, ActivityManager.getCurrentUser(), false /* force */);
    }

    private void handleUidActive(int uid) {
        updateActiveState(uid, true /* active */);
    }

    private void updateActiveState(int uid, boolean active) {
        synchronized (this) {
            for (int i = mPinnedApps.size() - 1; i >= 0; i--) {
                PinnedApp app = mPinnedApps.valueAt(i);
                if (app.uid == uid) {
                    app.active = active;
                }
            }
        }
    }

    private void unpinCameraApp() {
        ArrayList<PinnedFile> pinnedCameraFiles;
    private void unpinApp(@AppKey int key) {
        ArrayList<PinnedFile> pinnedAppFiles;
        synchronized (this) {
            pinnedCameraFiles = new ArrayList<>(mPinnedCameraFiles);
            mPinnedCameraFiles.clear();
            PinnedApp app = mPinnedApps.get(key);
            if (app == null) {
                return;
            }
        for (PinnedFile pinnedFile : pinnedCameraFiles) {
            mPinnedApps.remove(key);
            pinnedAppFiles = new ArrayList<>(app.mFiles);
        }
        for (PinnedFile pinnedFile : pinnedAppFiles) {
            pinnedFile.close();
        }
    }
@@ -206,64 +325,167 @@ public final class PinnerService extends SystemService {
        //  use INTENT_ACTION_STILL_IMAGE_CAMERA instead of _SECURE.  On a
        //  device without a fbe enabled, the _SECURE intent will never get set.
        Intent cameraIntent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
        PackageManager pm = mContext.getPackageManager();
        ResolveInfo cameraResolveInfo = pm.resolveActivityAsUser(cameraIntent,
                PackageManager.MATCH_DEFAULT_ONLY | PackageManager.MATCH_DIRECT_BOOT_AWARE
                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
                userHandle);
        if (cameraResolveInfo == null ) {
            //this is not necessarily an error
            if (DEBUG) {
              Slog.v(TAG, "Unable to resolve camera intent");
        return getApplicationInfoForIntent(cameraIntent, userHandle);
    }

    private ApplicationInfo getHomeInfo(int userHandle) {
        Intent intent = mAmInternal.getHomeIntent();
        return getApplicationInfoForIntent(intent, userHandle);
    }

    private ApplicationInfo getApplicationInfoForIntent(Intent intent, int userHandle) {
        if (intent == null) {
            return null;
        }
        ResolveInfo info = mContext.getPackageManager().resolveActivityAsUser(intent,
                MATCH_FLAGS, userHandle);
        if (info == null) {
            return null;
        }
        if (isResolverActivity(info.activityInfo)) {
            return null;
        }
        return info.activityInfo.applicationInfo;
    }

        if (isResolverActivity(cameraResolveInfo.activityInfo))
        {
            if (DEBUG) {
              Slog.v(TAG, "cameraIntent returned resolverActivity");
    private void sendPinAppsMessage(int userHandle) {
        mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::pinApps, this,
                userHandle));
    }

    private void pinApps(int userHandle) {
        for (int i = mPinKeys.size() - 1; i >= 0; i--) {
            int key = mPinKeys.valueAt(i);
            pinApp(key, userHandle, true /* force */);
        }
    }

    /**
     * @see #pinApp(int, int, boolean)
     */
    private void sendPinAppMessage(int key, int userHandle, boolean force) {
        mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::pinApp, this,
                key, userHandle, force));
    }

    /**
     * Pins an app of a specific type {@code key}.
     *
     * @param force If false, this will not repin the app if it's currently active. See
     *              {@link #mPendingRepin}.
     */
    private void pinApp(int key, int userHandle, boolean force) {
        int uid = getUidForKey(key);

        // In case the app is currently active, don't repin until next process restart. See
        // mPendingRepin for more information.
        if (!force && uid != -1) {
            synchronized (this) {
                mPendingRepin.put(uid, key);
            }
            return;
        }
        unpinApp(key);
        ApplicationInfo info = getInfoForKey(key, userHandle);
        if (info != null) {
            pinApp(key, info);
        }
    }

    /**
     * Checks whether the pinned package with {@code key} is active or not.

     * @return The uid of the pinned app, or {@code -1} otherwise.
     */
    private int getUidForKey(@AppKey int key) {
        synchronized (this) {
            PinnedApp existing = mPinnedApps.get(key);
            return existing != null && existing.active
                    ? existing.uid
                    : -1;
        }
    }

    /**
     * Retrieves the current application info for the given app type.
     *
     * @param key The app type to retrieve the info for.
     * @param userHandle The user id of the current user.
     */
    private @Nullable ApplicationInfo getInfoForKey(@AppKey int key, int userHandle) {
        switch (key) {
            case KEY_CAMERA:
                return getCameraInfo(userHandle);
            case KEY_HOME:
                return getHomeInfo(userHandle);
            default:
                return null;
        }
    }

        return cameraResolveInfo.activityInfo.applicationInfo;
    /**
     * @return The app type name for {@code key}.
     */
    private String getNameForKey(@AppKey int key) {
        switch (key) {
            case KEY_CAMERA:
                return "Camera";
            case KEY_HOME:
                return "Home";
            default:
                return null;
        }
    }

    /**
     * If the camera app is already pinned, unpin and repin it.
     * @return The maximum amount of bytes to be pinned for an app of type {@code key}.
     */
    private boolean pinCamera(int userHandle){
        ApplicationInfo cameraInfo = getCameraInfo(userHandle);
        if (cameraInfo == null) {
            return false;
    private int getSizeLimitForKey(@AppKey int key) {
        switch (key) {
            case KEY_CAMERA:
                return MAX_CAMERA_PIN_SIZE;
            case KEY_HOME:
                return MAX_HOME_PIN_SIZE;
            default:
                return 0;
        }
    }

        //unpin after checking that the camera intent has resolved
        //this prevents us from thrashing when switching users with
        //FBE enabled, because the intent won't resolve until the unlock
        unpinCameraApp();
    /**
     * Pins an application.
     *
     * @param key The key of the app to pin.
     * @param appInfo The corresponding app info.
     */
    private void pinApp(@AppKey int key, @Nullable ApplicationInfo appInfo) {
        if (appInfo == null) {
            return;
        }

        PinnedApp pinnedApp = new PinnedApp(appInfo);
        synchronized (this) {
            mPinnedApps.put(key, pinnedApp);
        }

        // pin APK
        String camAPK = cameraInfo.sourceDir;
        PinnedFile pf = pinFile(camAPK,
                                MAX_CAMERA_PIN_SIZE,
                                /*attemptPinIntrospection=*/true);
        int pinSizeLimit = getSizeLimitForKey(key);
        String apk = appInfo.sourceDir;
        PinnedFile pf = pinFile(apk, pinSizeLimit, /*attemptPinIntrospection=*/true);
        if (pf == null) {
            Slog.e(TAG, "Failed to pin " + camAPK);
            return false;
            Slog.e(TAG, "Failed to pin " + apk);
            return;
        }
        if (DEBUG) {
            Slog.i(TAG, "Pinned " + pf.fileName);
        }
        synchronized (this) {
            mPinnedCameraFiles.add(pf);
            pinnedApp.mFiles.add(pf);
        }

        // determine the ABI from either ApplicationInfo or Build
        String arch = "arm";
        if (cameraInfo.primaryCpuAbi != null) {
            if (VMRuntime.is64BitAbi(cameraInfo.primaryCpuAbi)) {
        if (appInfo.primaryCpuAbi != null) {
            if (VMRuntime.is64BitAbi(appInfo.primaryCpuAbi)) {
                arch = arch + "64";
            }
        } else {
@@ -273,32 +495,29 @@ public final class PinnerService extends SystemService {
        }

        // get the path to the odex or oat file
        String baseCodePath = cameraInfo.getBaseCodePath();
        String baseCodePath = appInfo.getBaseCodePath();
        String[] files = null;
        try {
            files = DexFile.getDexFileOutputPaths(baseCodePath, arch);
        } catch (IOException ioe) {}
        if (files == null) {
            return true;
            return;
        }

        //not pinning the oat/odex is not a fatal error
        for (String file : files) {
            pf = pinFile(file, MAX_CAMERA_PIN_SIZE, /*attemptPinIntrospection=*/false);
            pf = pinFile(file, pinSizeLimit, /*attemptPinIntrospection=*/false);
            if (pf != null) {
                synchronized (this) {
                    mPinnedCameraFiles.add(pf);
                    pinnedApp.mFiles.add(pf);
                }
                if (DEBUG) {
                    Slog.i(TAG, "Pinned " + pf.fileName);
                }
            }
        }

        return true;
    }


    /** mlock length bytes of fileToPin in memory
     *
     * If attemptPinIntrospection is true, then treat the file to pin as a zip file and
@@ -581,24 +800,38 @@ public final class PinnerService extends SystemService {
        }
    }

    private synchronized ArrayList<PinnedFile> snapshotPinnedFiles() {
        int nrPinnedFiles = mPinnedFiles.size() + mPinnedCameraFiles.size();
        ArrayList<PinnedFile> pinnedFiles = new ArrayList<>(nrPinnedFiles);
        pinnedFiles.addAll(mPinnedFiles);
        pinnedFiles.addAll(mPinnedCameraFiles);
        return pinnedFiles;
    }

    private final class BinderService extends Binder {
        @Override
        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
            if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
            synchronized (PinnerService.this) {
                long totalSize = 0;
            for (PinnedFile pinnedFile : snapshotPinnedFiles()) {
                for (PinnedFile pinnedFile : mPinnedFiles) {
                    pw.format("%s %s\n", pinnedFile.fileName, pinnedFile.bytesPinned);
                    totalSize += pinnedFile.bytesPinned;
                }
                pw.println();
                for (int key : mPinnedApps.keySet()) {
                    PinnedApp app = mPinnedApps.get(key);
                    pw.print(getNameForKey(key));
                    pw.print(" uid="); pw.print(app.uid);
                    pw.print(" active="); pw.print(app.active);
                    pw.println();
                    for (PinnedFile pf : mPinnedApps.get(key).mFiles) {
                        pw.print("  "); pw.format("%s %s\n", pf.fileName, pf.bytesPinned);
                        totalSize += pf.bytesPinned;
                    }
                }
                pw.format("Total size: %s\n", totalSize);
                pw.println();
                if (!mPendingRepin.isEmpty()) {
                    pw.print("Pending repin: ");
                    for (int key : mPendingRepin.values()) {
                        pw.print(getNameForKey(key)); pw.print(' ');
                    }
                    pw.println();
                }
            }
        }
    }

@@ -634,8 +867,30 @@ public final class PinnerService extends SystemService {
        int length;
    }

    /**
     * Represents an app that was pinned.
     */
    private final class PinnedApp {

        /**
         * The uid of the package being pinned. This stays constant while the package stays
         * installed.
         */
        final int uid;

        /** Whether it is currently active, i.e. there is a running process from that package. */
        boolean active;

        /** List of pinned files. */
        final ArrayList<PinnedFile> mFiles = new ArrayList<>();

        private PinnedApp(ApplicationInfo appInfo) {
            uid = appInfo.uid;
            active = mAmInternal.isUidActive(uid);
        }
    }

    final class PinnerHandler extends Handler {
        static final int PIN_CAMERA_MSG  = 4000;
        static final int PIN_ONSTART_MSG = 4001;

        public PinnerHandler(Looper looper) {
@@ -645,13 +900,6 @@ public final class PinnerService extends SystemService {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {

                case PIN_CAMERA_MSG:
                {
                    handlePinCamera(msg.arg1);
                }
                break;

                case PIN_ONSTART_MSG:
                {
                    handlePinOnStart();
+7 −0
Original line number Diff line number Diff line
@@ -22465,6 +22465,13 @@ public class ActivityManagerService extends IActivityManager.Stub
        public void finishUserSwitch(Object uss) {
            mUserController.finishUserSwitch((UserState) uss);
        }
        @Override
        public Intent getHomeIntent() {
            synchronized (ActivityManagerService.this) {
                return ActivityManagerService.this.getHomeIntent();
            }
        }
    }
    /**
Loading