Android在关机界面添加重启功能

Google原生的Android系统一般是没有“重启”这个选项的。有时候重启也是不可或缺的一个Feature,那么如何在源码环境下添加这个选项呢?


1. 在frameworks\base\core\res\res\values\strings.xml
中添加标签:

<span style="font-size:14px;"><!--zms add start-->
<!-- label for item that power reboot in phone options dialog -->
<string name="zzzzz_global_action_power_reboot">Reboot</string>
<!--zms add end--></span>

当然这只是英语语系的,需要添加其它语系的标示,把"Reboot" 替换成其它语言。


2. 在alps\frameworks\base\core\res\res\drawable-hdpi 中添加图标:
zzzzz_ic_lock_power_reboot.png


3. 打开frameworks\policies\base\phone\com\android\internal\policy\impl\GlobalActions.java
大概在这个文件的261行有这样的代码:

mItems = Lists.newArrayList(
// silent mode
mSilentModeToggle,
// next: airplane mode
mAirplaneModeOn,
// last: power off

在这里,我们添加power reboot 的新的item.
具体这个mItems 更新为如下:
mItems = Lists.newArrayList(
// silent mode
mSilentModeToggle,
// next: airplane mode
mAirplaneModeOn,
// last: power off
new SinglePressAction(
com.android.internal.R.drawable.ic_lock_power_off,
R.string.global_action_power_off) {
public void onPress() {
// shutdown by making sure radio and power are handled
accordingly.
ShutdownThread.shutdown(mContext, true);
}
public boolean showDuringKeyguard() {
return true;
}
public boolean showBeforeProvisioning() {
return true;
}
}//zms add start
,
new SinglePressAction(
com.android.internal.R.drawable.zzzzz_ic_lock_power_reboot,
R.string.zzzzz_global_action_power_reboot) {
public void onPress() {
// reboot by making sure radio and power are handled
accordingly.
ShutdownThread.reboot(mContext, null, true);
}
public boolean showDuringKeyguard() {
return true;
}
public boolean showBeforeProvisioning() {
return true;
} 
} 
//zms add end.
); 


经过这样的添加/修改后,这项feature 即可运行。
注意如果测试的话,因为有修改framework 中的文件,最好new 一下整个工程。
另外还需要修改一下ShutdownThread.java 中的那个dialog 显示描述,不然将依旧看到“关机”的信息。

位置:frameworks/base/services/java/com/android/server/power/ShutdownThread.java

不同版本的代码位置可能有所差别,可以在根目录下find一下:

find -name ShutdownThread.java

参照如下文件即可:

package com.android.server.power;

import android.app.ActivityManagerNative;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.IActivityManager;
import android.app.ProgressDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.IBluetoothManager;
import android.nfc.NfcAdapter;
import android.nfc.INfcAdapter;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.Vibrator;
import android.os.SystemVibrator;
import android.os.storage.IMountService;
import android.os.storage.IMountShutdownObserver;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.view.Surface;
import android.net.ConnectivityManager;

import com.android.internal.telephony.ITelephony;
import com.android.server.power.PowerManagerService;

import android.util.Log;
import android.view.WindowManager;
import android.view.IWindowManager;

// Wakelock
import android.os.PowerManager.WakeLock;

// For IPO
import com.android.internal.app.ShutdownManager;
import com.mediatek.common.featureoption.FeatureOption;
import android.provider.Settings;

import com.mediatek.common.bootanim.IBootAnimExt;
import com.mediatek.common.MediatekClassFactory;
import com.mediatek.common.hdmi.IHDMINative; // add for HDMI power control
import dalvik.system.Zygote;

public final class ShutdownThread extends Thread {
    // constants
    private static final String TAG = "ShutdownThread";
    private static final int PHONE_STATE_POLL_SLEEP_MSEC = 500;
    // maximum time we wait for the shutdown broadcast before going on.
    private static final int MAX_BROADCAST_TIME = 10*1000;
    private static final int MAX_SHUTDOWN_WAIT_TIME = 20*1000;
    private static final int MAX_RADIO_WAIT_TIME = 12*1000;

    // length of vibration before shutting down
    private static final int SHUTDOWN_VIBRATE_MS = 500;

    // state tracking
    private static Object sIsStartedGuard = new Object();
    private static boolean sIsStarted = false;

    private static boolean mReboot;
    private static boolean mRebootSafeMode;
    private static String mRebootReason;

    // Provides shutdown assurance in case the system_server is killed
    public static final String SHUTDOWN_ACTION_PROPERTY = "sys.shutdown.requested";

    // Indicates whether we are rebooting into safe mode
    public static final String REBOOT_SAFEMODE_PROPERTY = "persist.sys.safemode";

    // static instance of this thread
    private static final ShutdownThread sInstance = new ShutdownThread();

    private final Object mActionDoneSync = new Object();
    private boolean mActionDone;
    private Context mContext;
    private PowerManager mPowerManager;
    private PowerManager.WakeLock mCpuWakeLock;
    private PowerManager.WakeLock mScreenWakeLock;
    private Handler mHandler;

    private static AlertDialog sConfirmDialog = null;

    // IPO
    private static ProgressDialog pd = null;
    private static Object mShutdownThreadSync = new Object();
    private ShutdownManager stMgr = new ShutdownManager();

    // Shutdown Flow Settings
    private static final int NORMAL_SHUTDOWN_FLOW = 0x0;
    private static final int IPO_SHUTDOWN_FLOW = 0x1;
    private static int mShutdownFlow;

    // Shutdown Animation
    private static final int MIN_SHUTDOWN_ANIMATION_PLAY_TIME = 5*1000; // CU/CMCC operator require 3-5s
    private static long beginAnimationTime = 0;
    private static long endAnimationTime = 0;
    private static boolean bConfirmForAnimation = true;
    private static boolean bPlayaudio = true;

    private static final Object mEnableAnimatingSync = new Object();
    private static boolean mEnableAnimating = true;

    // length of waiting for memory dump if Modem Exception occurred
    private static final int MAX_MEMORY_DUMP_TIME = 60 * 1000;

    private static String command;  //for bypass radioOff
    private static int screen_turn_off_time = 5 * 1000;   //after 5sec  the screen become OFF, you can change the time delay

    private static IHDMINative ImHDMI = null; // for MFR

    private static final boolean mSpew = true;   //debug enable

    private static IBootAnimExt mIBootAnim = null; // for boot animation 

    private ShutdownThread() {
    }

    public static boolean isPowerOffDialogShowing() {
        return (sConfirmDialog != null && sConfirmDialog.isShowing());
    }

    public static void EnableAnimating(boolean enable) {
        synchronized (mEnableAnimatingSync) {
            mEnableAnimating = enable;
        }
    }

    /**
     * Request a clean shutdown, waiting for subsystems to clean up their
     * state etc.  Must be called from a Looper thread in which its UI
     * is shown.
     *
     * @param context Context used to display the shutdown progress dialog.
     * @param confirm true if user confirmation is needed before shutting down.
     */
    public static void shutdown(final Context context, boolean confirm) {
        mReboot = false;
        mRebootSafeMode = false;

        Log.d(TAG, "!!! Request to shutdown !!!");

        ShutdownManager.startFtraceCapture();

        if (mSpew) {
            StackTraceElement[] stack = new Throwable().getStackTrace();
            for (StackTraceElement element : stack)
            {
                Log.d(TAG, " 	|----" + element.toString());
            }
        }

        createPreShutdownApi(context);

        if (SystemProperties.getBoolean("ro.monkey", false)) {
            Log.d(TAG, "Cannot request to shutdown when Monkey is running, returning.");
            return;
        }

        shutdownInner(context, confirm);
    }

    static void shutdownInner(final Context context, boolean confirm) {
        // ensure that only one thread is trying to power down.
        // any additional calls are just returned
        synchronized (sIsStartedGuard) {
            if (sIsStarted) {
                Log.d(TAG, "Request to shutdown already running, returning.");
                return;
            }
        }

        Log.d(TAG, "Notifying thread to start radio shutdown");
        bConfirmForAnimation = confirm;
        final int longPressBehavior = context.getResources().getInteger(
                        com.android.internal.R.integer.config_longPressOnPowerBehavior);
        final int resourceId = (mReboot && !mRebootSafeMode)
	? com.android.internal.R.string.reboot_confirm
	: (mRebootSafeMode
                ? com.android.internal.R.string.reboot_safemode_confirm
                : (longPressBehavior == 2
                        ? com.android.internal.R.string.shutdown_confirm_question
                        : com.android.internal.R.string.shutdown_confirm));

        Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);

        if (confirm) {
            final CloseDialogReceiver closer = new CloseDialogReceiver(context);
            if (sConfirmDialog != null) {
                sConfirmDialog.dismiss();
            }
            if (sConfirmDialog == null) {
                Log.d(TAG, "PowerOff dialog doesn't exist. Create it first");
                sConfirmDialog = new AlertDialog.Builder(context)
                    .setTitle((mReboot && !mRebootSafeMode)
		    ? com.android.internal.R.string.reboot_title
	            : (mRebootSafeMode
                            ? com.android.internal.R.string.reboot_safemode_title
                            : com.android.internal.R.string.power_off))
                    .setMessage(resourceId)
                    .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int which) {
                            beginShutdownSequence(context);
                            if (sConfirmDialog != null) {
                            sConfirmDialog = null;
                            }
                            }
                            })
                .setNegativeButton(com.android.internal.R.string.no, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                        synchronized (sIsStartedGuard) {
                        sIsStarted = false;
                        }
                        if (sConfirmDialog != null) {
                        sConfirmDialog = null;
                        }
                        }
                        })
                .create();
                sConfirmDialog.setCancelable(false);//blocking back key
                sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
                /*if (!context.getResources().getBoolean(
                  com.android.internal.R.bool.config_sf_slowBlur)) {
                  sConfirmDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
                  }*/
                /* To fix video+UI+blur flick issue */
                sConfirmDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
            }

            closer.dialog = sConfirmDialog;
            sConfirmDialog.setOnDismissListener(closer);

            if (!sConfirmDialog.isShowing()) {
                sConfirmDialog.show();
            }
        } else {
            beginShutdownSequence(context);
        }
    }

    private static class CloseDialogReceiver extends BroadcastReceiver
            implements DialogInterface.OnDismissListener {
        private Context mContext;
        public Dialog dialog;

        CloseDialogReceiver(Context context) {
            mContext = context;
            IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
            context.registerReceiver(this, filter);
        }

        @Override
        public void onReceive(Context context, Intent intent) {
            Log.d(TAG, "CloseDialogReceiver: onReceive");
            dialog.cancel();
        }

        public void onDismiss(DialogInterface unused) {
            mContext.unregisterReceiver(this);
        }
    }

    private static Runnable mDelayDim = new Runnable() {   //use for animation, add by how.wang
        public void run() {
            Log.d(TAG, "setBacklightBrightness: Off");
            if (sInstance.mScreenWakeLock != null && sInstance.mScreenWakeLock.isHeld()) {
                sInstance.mScreenWakeLock.release();
                sInstance.mScreenWakeLock = null;
            }
            sInstance.mPowerManager.setBacklightBrightnessOff(true);
        }
    };

    /**
     * Request a clean shutdown, waiting for subsystems to clean up their
     * state etc.  Must be called from a Looper thread in which its UI
     * is shown.
     *
     * @param context Context used to display the shutdown progress dialog.
     * @param reason code to pass to the kernel (e.g. "recovery"), or null.
     * @param confirm true if user confirmation is needed before shutting down.
     */
    public static void reboot(final Context context, String reason, boolean confirm) {
        mReboot = true;
        mRebootSafeMode = false;
        mRebootReason = reason;
        Log.d(TAG, "reboot");

        if (mSpew) {
            StackTraceElement[] stack = new Throwable().getStackTrace();
            for (StackTraceElement element : stack)
            {
                Log.d(TAG, " 	|----" + element.toString());
            }
        }

        shutdownInner(context, confirm);
    }

    /**
     * Request a reboot into safe mode.  Must be called from a Looper thread in which its UI
     * is shown.
     *
     * @param context Context used to display the shutdown progress dialog.
     * @param confirm true if user confirmation is needed before shutting down.
     */
    public static void rebootSafeMode(final Context context, boolean confirm) {
        mReboot = true;
        mRebootSafeMode = true;
        mRebootReason = null;
        Log.d(TAG, "rebootSafeMode");
        shutdownInner(context, confirm);
    }

    private static void beginShutdownSequence(Context context) {
        synchronized (sIsStartedGuard) {
            if (sIsStarted) {
                Log.d(TAG, "Shutdown sequence already running, returning.");
                return;
            }
            sIsStarted = true;
        }

        // start the thread that initiates shutdown
        sInstance.mContext = context;
        sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
        sInstance.mHandler = new Handler() {
        };    

        bPlayaudio = true;
        if (!bConfirmForAnimation) {
            if (!sInstance.mPowerManager.isScreenOn()) {
                bPlayaudio = false;
            }
        }

        // throw up an indeterminate system dialog to indicate radio is
        // shutting down.
        beginAnimationTime = 0;
        boolean mShutOffAnimation = false;

        try {
            if (mIBootAnim == null) {
                mIBootAnim = MediatekClassFactory.createInstance(IBootAnimExt.class);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        int screenTurnOffTime = mIBootAnim.getScreenTurnOffTime();
        mShutOffAnimation = mIBootAnim.isCustBootAnim();
        Log.e(TAG, "mIBootAnim get screenTurnOffTime : " + screenTurnOffTime);

        String cust = SystemProperties.get("ro.operator.optr");

        if (cust != null) {
            if (cust.equals("CUST")) {
                mShutOffAnimation = true;
            }
        }

        synchronized (mEnableAnimatingSync) {

            if(!mEnableAnimating) {
                //sInstance.mPowerManager.setBacklightBrightness(PowerManager.BRIGHTNESS_DIM);
            } else {
                if (mShutOffAnimation) {
                    Log.e(TAG, "mIBootAnim.isCustBootAnim() is true");
                    bootanimCust();
                } else {
                    pd = new ProgressDialog(context);
                    pd.setTitle(context.getText(com.android.internal.R.string.power_off));
                    pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
                    pd.setIndeterminate(true);
                    pd.setCancelable(false);
                    pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
                    /* To fix video+UI+blur flick issue */
                    pd.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
                    pd.show();
                }
                sInstance.mHandler.postDelayed(mDelayDim, screenTurnOffTime); 
            }
        }

        // make sure we never fall asleep again
        sInstance.mCpuWakeLock = null;
        try {
            sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
                    PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
            sInstance.mCpuWakeLock.setReferenceCounted(false);
            sInstance.mCpuWakeLock.acquire();
        } catch (SecurityException e) {
            Log.w(TAG, "No permission to acquire wake lock", e);
            sInstance.mCpuWakeLock = null;
        }
        Log.d(TAG, "shutdown acquire partial WakeLock: cpu");

        // also make sure the screen stays on for better user experience
        sInstance.mScreenWakeLock = null;
        if (sInstance.mPowerManager.isScreenOn()) {
            try {
                sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
                        PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
                sInstance.mScreenWakeLock.setReferenceCounted(false);
                sInstance.mScreenWakeLock.acquire();
            } catch (SecurityException e) {
                Log.w(TAG, "No permission to acquire wake lock", e);
                sInstance.mScreenWakeLock = null;
            }
        }

        // start the thread that initiates shutdown
        if (sInstance.getState() != Thread.State.NEW || sInstance.isAlive()) {
            if (mShutdownFlow == IPO_SHUTDOWN_FLOW) {
                Log.d(TAG, "ShutdownThread exists already");
                checkShutdownFlow();
                synchronized (mShutdownThreadSync) {
                    mShutdownThreadSync.notify();
                }
            } else {
                Log.e(TAG, "Thread state is not normal! froce to shutdown!");
                delayForPlayAnimation();
                //unmout data/cache partitions while performing shutdown    
                //Power.shutdown();
                SystemProperties.set("ctl.start", "shutdown");
            }
        } else {
            sInstance.start();
        }
    }

    private static void bootanimCust() {
        // [MTK] fix shutdown animation timing issue
        //==================================================================
        SystemProperties.set("service.shutanim.running","0");
        Log.i(TAG, "set service.shutanim.running to 0");
        //==================================================================
        boolean isRotaionEnabled = false;
        try {
            isRotaionEnabled = Settings.System.getInt(sInstance.mContext.getContentResolver(),
                    Settings.System.ACCELEROMETER_ROTATION, 1) != 0;
            if (isRotaionEnabled) {
                final IWindowManager wm = IWindowManager.Stub.asInterface(
                        ServiceManager.getService(Context.WINDOW_SERVICE));
                wm.freezeRotation(Surface.ROTATION_0);
                Settings.System.putInt(sInstance.mContext.getContentResolver(),
                        Settings.System.ACCELEROMETER_ROTATION, 0);
                Settings.System.putInt(sInstance.mContext.getContentResolver(),
                        Settings.System.ACCELEROMETER_ROTATION_RESTORE, 1);
            }
        } catch (NullPointerException ex) {
            Log.e(TAG, "check Rotation: sInstance.mContext object is null when get Rotation");
        } catch (RemoteException e) {
        }
        beginAnimationTime = SystemClock.elapsedRealtime() + MIN_SHUTDOWN_ANIMATION_PLAY_TIME;
        // +MediaTek 2012-02-25 Disable key dispatch
        try {
            final IWindowManager wm = IWindowManager.Stub.asInterface(
                    ServiceManager.getService(Context.WINDOW_SERVICE));
            wm.setEventDispatching(false);
        } catch (RemoteException e) {}
        // -MediaTek 2012-02-25 Disable key dispatch
        startBootAnimation();
    }

    private static void startBootAnimation() {

        try {
            SystemProperties.set("service.bootanim.exit","0");
            Log.d(TAG, "Set 'service.bootanim.exit' = 0).");
        } catch (Exception ex) {
            Log.e(TAG, "Failed to set 'service.bootanim.exit' = 0).");
        }   

        if (bPlayaudio) {
            SystemProperties.set("ctl.start","bootanim:shut mp3");
            Log.d(TAG, "bootanim:shut mp3" );
        } else {
            SystemProperties.set("ctl.start","bootanim:shut nomp3");
            Log.d(TAG, "bootanim:shut nomp3" );
        }
    }

    void actionDone() {
        synchronized (mActionDoneSync) {
            mActionDone = true;
            mActionDoneSync.notifyAll();
        }
    }

    private static void delayForPlayAnimation() {
        if (beginAnimationTime <= 0) {
            return;
        }
        endAnimationTime = beginAnimationTime - SystemClock.elapsedRealtime();
        if (endAnimationTime > 0) {
            try {
                Thread.currentThread().sleep(endAnimationTime);
            } catch ( Exception e) {
                Log.e(TAG, "Shutdown stop bootanimation Thread.currentThread().sleep exception!");
            }
        }
    }

    /*
     * Please make sure that context object is already instantiated already before calling this method.
     * However, we'll still catch null pointer exception here in case.
     */
    private static void checkShutdownFlow() {
        Log.d(TAG, "checkShutdownFlow: IPO_Support=" + FeatureOption.MTK_IPO_SUPPORT + " mReboot=" + mReboot);
        if (FeatureOption.MTK_IPO_SUPPORT == false || mReboot == true || Zygote.systemInSafeMode == true) {
            mShutdownFlow = NORMAL_SHUTDOWN_FLOW;
            return;
        }

        boolean isIPOEnabled;
        
        try {
            isIPOEnabled = Settings.System.getInt(sInstance.mContext.getContentResolver(),
                    Settings.System.IPO_SETTING, 1) == 1;
        } catch (NullPointerException ex) {
            Log.e(TAG, "checkShutdownFlow: sInstance.mContext object is null when get IPO enable/disable Option");
            mShutdownFlow = NORMAL_SHUTDOWN_FLOW;
            return;
        }

        if (isIPOEnabled == true) {
            if ("1".equals(SystemProperties.get("sys.ipo.battlow")))
                mShutdownFlow = NORMAL_SHUTDOWN_FLOW;
            else
                mShutdownFlow = IPO_SHUTDOWN_FLOW;
        } else {
            mShutdownFlow = NORMAL_SHUTDOWN_FLOW;
        }

        // power off auto test, don't modify
        Log.d(TAG, "checkShutdownFlow: isIPOEnabled=" + isIPOEnabled + " mShutdownFlow=" + mShutdownFlow);
        return;
    }

    /**
     * Makes sure we handle the shutdown gracefully.
     * Shuts off power regardless of radio and bluetooth state if the alloted time has passed.
     */
    public void run() {
        checkShutdownFlow();
        while (mShutdownFlow == IPO_SHUTDOWN_FLOW) {
            stMgr.saveStates(mContext);
            stMgr.enterShutdown(mContext);
            running();
        }
        if (mShutdownFlow != IPO_SHUTDOWN_FLOW) {
            stMgr.enterShutdown(mContext);
            running();
        }
    }

    public void running() {
        if(sPreShutdownApi != null){
            try {
                sPreShutdownApi.onPowerOff();
            } catch (RemoteException e) {
                Log.e(TAG, "onPowerOff exception" + e.getMessage());
            }
        }else{
            Log.w(TAG, "sPreShutdownApi is null");
        }

        command = SystemProperties.get("sys.ipo.pwrdncap");

        BroadcastReceiver br = new BroadcastReceiver() {
            @Override public void onReceive(Context context, Intent intent) {
                // We don't allow apps to cancel this, so ignore the result.
                actionDone();
            }
        };

        /*
         * Write a system property in case the system_server reboots before we
         * get to the actual hardware restart. If that happens, we'll retry at
         * the beginning of the SystemServer startup.
         */
        {
            String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
            SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
        }

        /*
         * If we are rebooting into safe mode, write a system property
         * indicating so.
         */
        if (mRebootSafeMode) {
            SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
        }

        Log.i(TAG, "Sending shutdown broadcast...");

        // First send the high-level shut down broadcast.
        mActionDone = false;
        /// M: 2012-05-20 ALPS00286063 @{
        mContext.sendBroadcast(new Intent("android.intent.action.ACTION_PRE_SHUTDOWN"));
        /// @} 2012-05-20
        mContext.sendOrderedBroadcastAsUser((new Intent()).setAction(Intent.ACTION_SHUTDOWN).putExtra("_mode", mShutdownFlow),
                UserHandle.ALL, null, br, mHandler, 0, null, null);

        final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
        synchronized (mActionDoneSync) {
            while (!mActionDone) {
                long delay = endTime - SystemClock.elapsedRealtime();
                if (delay <= 0) {
                    Log.w(TAG, "Shutdown broadcast ACTION_SHUTDOWN timed out");
                    if (mShutdownFlow == IPO_SHUTDOWN_FLOW) {
                        Log.d(TAG, "change shutdown flow from ipo to normal: ACTION_SHUTDOWN timeout");
                        mShutdownFlow = NORMAL_SHUTDOWN_FLOW;
                    }
                    break;
                }
                try {
                    mActionDoneSync.wait(delay);
                } catch (InterruptedException e) {
                }
            }
        }

        // Also send ACTION_SHUTDOWN_IPO in IPO shut down flow
        if (mShutdownFlow == IPO_SHUTDOWN_FLOW) {
            mActionDone = false;
            mContext.sendOrderedBroadcast(new Intent("android.intent.action.ACTION_SHUTDOWN_IPO"), null,
                    br, mHandler, 0, null, null);
            final long endTimeIPO = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
            synchronized (mActionDoneSync) {
                while (!mActionDone) {
                    long delay = endTimeIPO - SystemClock.elapsedRealtime();
                    if (delay <= 0) {
                        Log.w(TAG, "Shutdown broadcast ACTION_SHUTDOWN_IPO timed out");
                        if (mShutdownFlow == IPO_SHUTDOWN_FLOW) {
                            Log.d(TAG, "change shutdown flow from ipo to normal: ACTION_SHUTDOWN_IPO timeout");
                            mShutdownFlow = NORMAL_SHUTDOWN_FLOW;
                        }
                        break;
                    }
                    try {
                        mActionDoneSync.wait(delay);
                    } catch (InterruptedException e) {
                    }
                }
            }
        }

        if (mShutdownFlow != IPO_SHUTDOWN_FLOW) {
            // power off auto test, don't modify
            Log.i(TAG, "Shutting down activity manager...");

            final IActivityManager am =
                ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
            if (am != null) {
                try {
                    am.shutdown(MAX_BROADCAST_TIME);
                } catch (RemoteException e) {
                }
            }
        }

        // power off auto test, don't modify
        // Shutdown radios.
        Log.i(TAG, "Shutting down radios...");
        shutdownRadios(MAX_RADIO_WAIT_TIME);

        // power off auto test, don't modify
        Log.i(TAG, "Shutting down MountService...");
        if ( (mShutdownFlow == IPO_SHUTDOWN_FLOW) && (command.equals("1")||command.equals("3")) ) {
            Log.i(TAG, "bypass MountService!");
        } else {
            // Shutdown MountService to ensure media is in a safe state
            IMountShutdownObserver observer = new IMountShutdownObserver.Stub() {
                public void onShutDownComplete(int statusCode) throws RemoteException {
                    Log.w(TAG, "Result code " + statusCode + " from MountService.shutdown");
                    if (statusCode < 0) {
                        mShutdownFlow = NORMAL_SHUTDOWN_FLOW; 
                    }
                    actionDone();
                }
            };

            // Set initial variables and time out time.
            mActionDone = false;
            final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
            synchronized (mActionDoneSync) {
                try {
                    final IMountService mount = IMountService.Stub.asInterface(
                            ServiceManager.checkService("mount"));
                    if (mount != null) {
                        mount.shutdown(observer);
                    } else {
                        Log.w(TAG, "MountService unavailable for shutdown");
                    }
                } catch (Exception e) {
                    Log.e(TAG, "Exception during MountService shutdown", e);
                }
                while (!mActionDone) {
                    long delay = endShutTime - SystemClock.elapsedRealtime();
                    if (delay <= 0) {
                        Log.w(TAG, "Shutdown wait timed out");
                        if (mShutdownFlow == IPO_SHUTDOWN_FLOW) {
                            Log.d(TAG, "change shutdown flow from ipo to normal: MountService");
                            mShutdownFlow = NORMAL_SHUTDOWN_FLOW;
                        }
                        break;
                    }
                    try {
                        mActionDoneSync.wait(delay);
                    } catch (InterruptedException e) {
                    }
                }
            }
        }

        // power off auto test, don't modify
        //mountSerivce 
        Log.i(TAG, "MountService shut done...");
        // [MTK] fix shutdown animation timing issue
        //==================================================================
        try {
            SystemProperties.set("service.shutanim.running","1");
            Log.i(TAG, "set service.shutanim.running to 1");

        } catch (Exception ex) {
            Log.e(TAG, "Failed to set 'service.shutanim.running' = 1).");
        }
        //==================================================================

        if (mShutdownFlow == IPO_SHUTDOWN_FLOW) {
            if (SHUTDOWN_VIBRATE_MS > 0) {
                // vibrate before shutting down
                Vibrator vibrator = new SystemVibrator();
                try {
                    vibrator.vibrate(SHUTDOWN_VIBRATE_MS);
                } catch (Exception e) {
                    // Failure to vibrate shouldn't interrupt shutdown.  Just log it.
                    Log.w(TAG, "Failed to vibrate during shutdown.", e);
                }

                // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
                try {
                    Thread.sleep(SHUTDOWN_VIBRATE_MS);
                } catch (InterruptedException unused) {
                }
            }

            // Shutdown power
            // power off auto test, don't modify
            Log.i(TAG, "Performing ipo low-level shutdown...");

            delayForPlayAnimation();

            if (sInstance.mScreenWakeLock != null && sInstance.mScreenWakeLock.isHeld()) {
                sInstance.mScreenWakeLock.release();
                sInstance.mScreenWakeLock = null;
            }

            sInstance.mHandler.removeCallbacks(mDelayDim);
            stMgr.shutdown(mContext);
            stMgr.finishShutdown(mContext);
            ShutdownManager.stopFtraceCapture();

            //To void previous UI flick caused by shutdown animation stopping before BKL turning off
            if (pd != null) {
                pd.dismiss();
                pd = null;
            } else if (beginAnimationTime > 0) {
                try {
                    SystemProperties.set("service.bootanim.exit","1");
                    Log.i(TAG, "set 'service.bootanim.exit' = 1).");
                } catch (Exception ex) {
                    Log.e(TAG, "Failed to set 'service.bootanim.exit' = 1).");
                }  
                //SystemProperties.set("ctl.stop","bootanim");
            }

            synchronized (sIsStartedGuard) {
                sIsStarted = false;
            }

            sInstance.mPowerManager.setBacklightBrightnessOff(false); 
            sInstance.mCpuWakeLock.acquire(2000); 

            synchronized (mShutdownThreadSync) {
                try {
                    mShutdownThreadSync.wait();
                } catch (InterruptedException e) {
                }
            }
        } else {
            ShutdownManager.stopFtraceCapture();
            rebootOrShutdown(mReboot, mRebootReason);
        }
    }

    private void shutdownRadios(int timeout) {
        ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
        final boolean bypassRadioOff = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false ||
            ( (mShutdownFlow == IPO_SHUTDOWN_FLOW) && (command.equals("2")||command.equals("3")) );

        // If a radio is wedged, disabling it may hang so we do this work in another thread,
        // just in case.
        final long endTime = SystemClock.elapsedRealtime() + timeout;
        final boolean[] done = new boolean[1];
        Thread t = new Thread() {
            public void run() {
                boolean nfcOff;
                boolean bluetoothOff;
                boolean radioOff;

                Log.w(TAG, "task run");

                final INfcAdapter nfc =
                    INfcAdapter.Stub.asInterface(ServiceManager.checkService("nfc"));
                final ITelephony phone =
                    ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
                final IBluetoothManager bluetooth =
                        IBluetoothManager.Stub.asInterface(ServiceManager.checkService(
                                BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE));

                Log.w(TAG, "Turning off NFC");

                try {
                    nfcOff = nfc == null ||
                        nfc.getState() == NfcAdapter.STATE_OFF;
                    if (!nfcOff) {
                        Log.w(TAG, "Turning off NFC...");
                        nfc.disable(false); // Don't persist new state
                    }
                } catch (RemoteException ex) {
                    Log.e(TAG, "RemoteException during NFC shutdown", ex);
                    nfcOff = true;
                }

                Log.w(TAG, "Turning off BT");

                try {
                    bluetoothOff = bluetooth == null || !bluetooth.isEnabled();
                    if (!bluetoothOff) {
                        Log.w(TAG, "Disabling Bluetooth...");
                        bluetooth.disable(false);  // disable but don't persist new state
                    }
                } catch (RemoteException ex) {
                    Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
                    bluetoothOff = true;
                }

                try {
                    radioOff = phone == null || !phone.isRadioOn();
                    if (mShutdownFlow != IPO_SHUTDOWN_FLOW) {
                        //if (!radioOff) {
                        //Log.w(TAG, "Turning off radio...");
                        //phone.setRadio(false);
                        //} 

                        // Should always trigger modem shutdown
                        Log.w(TAG, "Turning off radio...");
                        //Judge whether "phone" is ready. 
                        //If we power on under low power condition, Shutdown may run here without adding "phone" service to system yet.
                        //With this scerario, phone object is null pointer.
                        if (phone != null) {
                            phone.setRadioOff();
                        }
                    }
                } catch (RemoteException ex) {
                    Log.e(TAG, "RemoteException during radio shutdown", ex);
                    radioOff = true;
                }

                Log.i(TAG, "Waiting for NFC, Bluetooth and Radio...");

                if (bypassRadioOff) {
                    done[0] = true;
                    Log.i(TAG, "bypass RadioOff!");
                } else {
                    while (SystemClock.elapsedRealtime() < endTime) {
                        if (!bluetoothOff) {
                            try {
                                bluetoothOff = !bluetooth.isEnabled();
                            } catch (RemoteException ex) {
                                Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
                                bluetoothOff = true;
                            }
                            if (bluetoothOff) {
                                Log.i(TAG, "Bluetooth turned off.");
                            }
                        }
                        if (!radioOff) {
                            try {
                                radioOff = !phone.isRadioOn();
                            } catch (RemoteException ex) {
                                Log.e(TAG, "RemoteException during radio shutdown", ex);
                                radioOff = true;
                            }
                            if (radioOff) {
                                Log.i(TAG, "Radio turned off.");
                            }
                        }
                        if (!nfcOff) {
                            try {
                                nfcOff = nfc.getState() == NfcAdapter.STATE_OFF;
                            } catch (RemoteException ex) {
                                Log.e(TAG, "RemoteException during NFC shutdown", ex);
                                nfcOff = true;
                            }
                            if (radioOff) {
                                Log.i(TAG, "NFC turned off.");
                            }
                        }

                        if (radioOff && bluetoothOff && nfcOff) {
                            Log.i(TAG, "NFC, Radio and Bluetooth shutdown complete.");
                            done[0] = true;
                            break;
                        }
                        SystemClock.sleep(PHONE_STATE_POLL_SLEEP_MSEC);
                    }
                }
            }
        };

        t.start();
        try {
            t.join(timeout);
        } catch (InterruptedException ex) {
        }
        if (!done[0]) {
            Log.w(TAG, "Timed out waiting for NFC, Radio and Bluetooth shutdown.");
            if (mShutdownFlow == IPO_SHUTDOWN_FLOW) {
                Log.d(TAG, "change shutdown flow from ipo to normal: BT/MD");
                mShutdownFlow = NORMAL_SHUTDOWN_FLOW;
            }
            if (SystemProperties.get("debug.mdlogger.Running").equals("1")) {
                Log.d(TAG, "radioOff = false and mdlogger is running now, so wait for memory dump");
                //SystemClock.sleep(Integer.MAX_VALUE);     /* endless wait */
                SystemClock.sleep(MAX_MEMORY_DUMP_TIME);
            }
        }
    }

    /**
     * Do not call this directly. Use {@link #reboot(Context, String, boolean)}
     * or {@link #shutdown(Context, boolean)} instead.
     *
     * @param reboot true to reboot or false to shutdown
     * @param reason reason for reboot
     */
    public static void rebootOrShutdown(boolean reboot, String reason) {
        if (reboot) {
            Log.i(TAG, "Rebooting, reason: " + reason);
            if ( (reason != null) && reason.equals("recovery") ) {
                delayForPlayAnimation();
            }
            try {
                PowerManagerService.lowLevelReboot(reason);
            } catch (Exception e) {
                Log.e(TAG, "Reboot failed, will attempt shutdown instead", e);
            }
        } else if (SHUTDOWN_VIBRATE_MS > 0) {
            // vibrate before shutting down
            Vibrator vibrator = new SystemVibrator();
            try {
                vibrator.vibrate(SHUTDOWN_VIBRATE_MS);
            } catch (Exception e) {
                // Failure to vibrate shouldn't interrupt shutdown.  Just log it.
                Log.w(TAG, "Failed to vibrate during shutdown.", e);
            }

            // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
            try {
                Thread.sleep(SHUTDOWN_VIBRATE_MS);
            } catch (InterruptedException unused) {
            }
        }

        delayForPlayAnimation();
        // Shutdown power
        // power off auto test, don't modify
        Log.i(TAG, "Performing low-level shutdown...");
        //PowerManagerService.lowLevelShutdown();
        //add your func: HDMI off
        //add for MFR
        try {
            if (ImHDMI == null)
                ImHDMI=MediatekClassFactory.createInstance(IHDMINative.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        ImHDMI.hdmiPowerEnable(false);

        //unmout data/cache partitions while performing shutdown

        SystemProperties.set("ctl.start", "shutdown");

        /* sleep for a long time, prevent start another service */
        try {
            Thread.currentThread().sleep(Integer.MAX_VALUE);
        } catch ( Exception e) {
            Log.e(TAG, "Shutdown rebootOrShutdown Thread.currentThread().sleep exception!");
        }
    }

    private static IPreShutdown sPreShutdownApi = null;

    public static void createPreShutdownApi(Context context) {
	    Log.d(TAG, "createPreShutdownApi(), context = " + context);
        Intent intent = new Intent();
        intent.setAction("com.mediatek.IPreShutdown");
        if (context != null) {
            Log.w(TAG, "bindservice:" + intent.getAction());
            context.bindService(intent, sPreShutdownConnection, 0);
        } else {
            Log.w(TAG, "context is null, cannot bindservice");
        }
       
    }

    protected static ServiceConnection sPreShutdownConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            Log.v(TAG, "onServiceConnected() entry");
            sPreShutdownApi = IPreShutdown.Stub.asInterface(service);
            Log.v(TAG, "onServiceConnected() exit");
        }

        @Override
        public void onServiceDisconnected(ComponentName className) {
            Log.v(TAG, "onServiceDisconnected() entry");
            sPreShutdownApi = null;
            Log.v(TAG, "onServiceDisconnected() exit");
        }
    };   
}


转载请注明出处:周木水的CSDN博客 http://blog.csdn.net/zhoumushui

我的GitHub:周木水的GitHub https://github.com/zhoumushui


郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。