Статьи

В глубине: последовательность выключения Android

  • Что случилось, когда я долго нажимал кнопку питания?
  • Что такое последовательность выключения?
  • Чем он отличается от последовательности выключения рабочего стола Linux?
  • Как изменить меню выключения?

Когда мы думаем о последовательности выключения Android, всплывают многие вопросы. Прежде чем читать о последовательности выключения, я предлагаю вам прочитать статью о последовательности загрузки .

Android — операционная система с открытым исходным кодом на основе linux, x86 (x86 — это серия архитектур наборов команд компьютерного микропроцессора, основанная на процессоре Intel 8086.), скорее всего, это система, в которой развернуто ядро ​​linux, однако все устройства Android работают в процессе ARM (ARM ( ранее Advanced RISC Machine, которая ранее была Acorn RISC Machine)), за исключением устройства Intel Xolo ( http://xolo.in/xolo-x900-features ). Xolo поставляется с процессором Atom 1.6 ГГц x86. Последовательность выключения Android отличается от настольных Linux, таких как Ubuntu, Fedora и т. Д.

В этой статье я собираюсь объяснить последовательность выключения только для Android. Пожалуйста, обратитесь к «Linux Boot and Shutdown Process» для получения подробной информации о процессе выключения Linux на рабочем столе.

Следующая диаграмма подробно иллюстрирует последовательность выключения.

Последовательность выключения Android

Шаг 1: Нажмите и удерживайте кнопку питания в течение 500 мс.

Шаг 2: PhoneWindowManager.java идентифицирует кнопку питания длительным нажатием и вызывает метод с именем «interceptKeyBeforeQueueing».

Следующий код отображает фрагмент кнопки питания из функции.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/** {@inheritDoc} */
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {
....
....
....
case KeyEvent.KEYCODE_POWER: {
     result &= ~ACTION_PASS_TO_USER;
       if (down) {
         if (isScreenOn && !mPowerKeyTriggered
               && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
                   mPowerKeyTriggered = true;
                   mPowerKeyTime = event.getDownTime();
                   interceptScreenshotChord();
            }
               ITelephony telephonyService = getTelephonyService();
                boolean hungUp = false;
               if (telephonyService != null) {
                   try {
                       if (telephonyService.isRinging()) {
                           // Pressing Power while there's a ringing incoming
                           // call should silence the ringer.
                            telephonyService.silenceRinger();
                       } else if ((mIncallPowerBehavior
                                & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
                               && telephonyService.isOffhook()) {
                            // Otherwise, if "Power button ends call" is enabled,
                           // the Power button will hang up any current active call.
                            hungUp = telephonyService.endCall();
                       }
                   } catch (RemoteException ex) {
                        Log.w(TAG, "ITelephony threw RemoteException", ex);
                   }
               }
               interceptPowerKeyDown(!isScreenOn || hungUp
                       || mVolumeDownKeyTriggered || mVolumeUpKeyTriggered);
           } else {
               mPowerKeyTriggered = false;
               cancelPendingScreenshotChordAction();
               if (interceptPowerKeyUp(canceled || mPendingPowerKeyUpCanceled)) {
                   result = (result & ~ACTION_WAKE_UP) | ACTION_GO_TO_SLEEP;
               }
               mPendingPowerKeyUpCanceled = false;
           }
          break;
      }
....
....
....
}

Выше кода обрабатывать несколько вариантов, таких как рингтон тишины, снимки экрана и отключение питания. Он определит соответствующую опцию на основе продолжительности времени и состояния другого ключа. Он будет вызывать параметр «interceptPowerKeyDown», исключая другие параметры.

Следующий код отображает функцию interceptPowerKeyDown. Это будет ждать 500 миллисекунд (ViewConfiguration # getGlobalActionKeyTimeout ())
затем вызовите поток mPowerLongPress.

1
2
3
4
5
6
private void interceptPowerKeyDown(boolean handled) {
  mPowerKeyHandled = handled;
  if (!handled) {
       mHandler.postDelayed(mPowerLongPress, ViewConfiguration.getGlobalActionKeyTimeout());
  }
}

Следующий код представляет поток mPowerLongPress

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
private final Runnable mPowerLongPress = new Runnable() {
        @Override
        public void run() {
            // The context isn't read
            if (mLongPressOnPowerBehavior < 0) {
                mLongPressOnPowerBehavior = mContext.getResources().getInteger(
                        com.android.internal.R.integer.config_longPressOnPowerBehavior);
            }
            int resolvedBehavior = mLongPressOnPowerBehavior;
            if (FactoryTest.isLongPressOnPowerOffEnabled()) {
                resolvedBehavior = LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM;
            }
 
            switch (resolvedBehavior) {
            case LONG_PRESS_POWER_NOTHING:
                break;
            case LONG_PRESS_POWER_GLOBAL_ACTIONS:
                mPowerKeyHandled = true;
                if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) {
                    performAuditoryFeedbackForAccessibilityIfNeed();
                }
                sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
                showGlobalActionsDialog();
                break;
            case LONG_PRESS_POWER_SHUT_OFF:
            case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
                mPowerKeyHandled = true;
                performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
                sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
                mWindowManagerFuncs.shutdown(resolvedBehavior == LONG_PRESS_POWER_SHUT_OFF);
                break;
            }
        }
    };

Шаг 3: Управление переходит к GlobalActions.java, который отвечает за отображение диалогового окна с различными параметрами, такими как (Выключение питания, Режим полета, Снимок экрана и несколько кнопок переключения). Это диалоговое окно имеет различные параметры в зависимости от поставщика OEM, модели и ОС Android. версия. Класс GlobalAction имеет метод с именем showdialog (), который отвечает за создание объекта Dialogbox с параметрами.

01
02
03
04
05
06
07
08
09
10
11
12
void showGlobalActionsDialog() {
    if (mGlobalActions == null) {
        mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
    }
    final boolean keyguardShowing = keyguardIsShowingTq();
    mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
    if (keyguardShowing) {
         // since it took two seconds of long press to bring this up,
        // poke the wake lock so they have some time to see the dialog.
        mKeyguardMediator.userActivity();
    }
}

Screenshot_2013-08-16-10-47-09

Шаг 4: Если пользователь выберет опцию «Выключить» в диалоговом окне, то элемент управления снова вернется к PhoneWindowManager, он начнет процесс завершения работы.

Шаг 5: Процесс выключения запускается из функции shoutdowninner () файла ShutdownThread.java. Он отобразит диалоговое окно подтверждения с помощью кнопки ok / cancel. Если пользователь выберет опцию ok, тогда начнется фактический процесс выключения.

Screenshot_2013-08-16-10-47-29

Шаг 6: функция beginShutdownSequence () вызывается, когда пользователь выбирает опцию OK в диалоговом окне.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
private static void beginShutdownSequence(Context context) {
        synchronized (sIsStartedGuard) {
            if (sIsStarted) {
                Log.d(TAG, "Shutdown sequence already running, returning.");
                return;
            }
            sIsStarted = true;
        }
 
        // throw up an indeterminate system dialog to indicate radio is
        // shutting down.
        ProgressDialog 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);
 
        pd.show();
 
        sInstance.mContext = context;
        sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
 
        // 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;
        }
 
        // 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
        sInstance.mHandler = new Handler() {
        };
        sInstance.start();
    }

Запустить метод, запустить фактический процесс выключения

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
public void run() {
        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;
        Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        mContext.sendOrderedBroadcastAsUser(intent,
                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 timed out");
                    break;
                }
                try {
                    mActionDoneSync.wait(delay);
                } catch (InterruptedException e) {
                }
            }
        }
 
        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) {
            }
        }
 
        // Shutdown radios.
        shutdownRadios(MAX_RADIO_WAIT_TIME);
 
        // 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");
                actionDone();
            }
        };
 
        Log.i(TAG, "Shutting down MountService");
 
        // 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");
                    break;
                }
                try {
                    mActionDoneSync.wait(delay);
                } catch (InterruptedException e) {
                }
            }
        }
 
        rebootOrShutdown(mReboot, mRebootReason);
    }

Шаг 7: С помощью метода rebootOrShutdown () управление переходит в нативную функцию файла com_android_server_power_PowerManagerService.cpp, и, наконец, управление переходит в файл android_reboot.c, который является последним этапом последовательности завершения работы.

1
2
3
static void nativeShutdown(JNIEnv *env, jclass clazz) {
    android_reboot(ANDROID_RB_POWEROFF, 0, 0);
}

Ресурсы

  1. PhoneWindowManager.java
  2. GlobalActions.java
  3. WindowManagerPolicy.java
  4. ShutdownThread.java
  5. PowerManagerService.java
  6. com_android_server_power_PowerManagerService.cpp
  7. android_reboot.c
  8. Процесс загрузки и завершения работы Linux

Ссылка: в глубине: последовательность завершения работы Android от нашего партнера JCG Кетана Пармара в блоге KP Bird .