M7350/base/services/java/com/android/server/SuspendActivity.java
2024-09-09 08:52:07 +00:00

326 lines
13 KiB
Java

/*
* Copyright (c) 2011, 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;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.PowerManager;
import android.provider.Settings;
import android.os.SystemClock;
import android.content.BroadcastReceiver;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.content.Context;
import android.view.KeyEvent;
import android.app.ActivityManager;
import java.util.List;
import android.content.res.Configuration;
import android.view.Display;
import android.os.SystemProperties;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* SuspendAcitivity provides the Warmboot feature which enables Device to enter into
* and resume from "suspend2ram" state
*/
public class SuspendActivity extends Activity {
private static final String TAG = "SuspendActivity";
private static final String INPUT_METHOD_SERVICE = "com.android.inputmethod.latin";
// Airplane Mode needs to be set to enter "TCXO Shutdown" (complete-suspend) state. With out this, device can enter only "Power-collapse" state
private boolean mAirplaneModeSetBy = false;
// Indicates whether this Activity was already paused or not */
private boolean mWasPaused = false;
// Indicates whether this Activity was already resumed & state-restored or not
private boolean mStateRestored = false;
// Splash screen events
private static final int START_BOOT_SPLASH = 1;
private static final int STOP_BOOT_SPLASH = 2;
// Splash screen state handling
private SplashHandler mHandler = new SplashHandler();
class SplashHandler extends Handler {
/**
* Handler's Main function which looks after the transitions
*/
public void handleMessage(Message msg) {
switch( msg.what ) {
case START_BOOT_SPLASH:
Log.d(TAG, "handleMessage() Event : START_BOOT_SPLASH");
SystemProperties.set( "ctl.start", "bootanim");
break;
case STOP_BOOT_SPLASH:
Log.d(TAG, "handleMessage() Event : STOP_BOOT_SPLASH");
SystemProperties.set( "ctl.stop", "bootanim");
break;
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Display getOrient = getWindowManager().getDefaultDisplay();
mWasPaused = false;
Log.d(TAG, "onCreate() : " + SystemClock.elapsedRealtime() + " WIDTH = " + getOrient.getWidth() + ", HEIGHT = " + getOrient.getHeight() );
setSuspendState();
}
@Override
protected void onStart( ) {
super.onStart( );
Log.d(TAG, "onStart() : " + SystemClock.elapsedRealtime());
}
@Override
protected void onRestart( ) {
super.onRestart( );
Log.d(TAG, "onRestart() : " + SystemClock.elapsedRealtime());
}
@Override
protected void onResume( ) {
super.onResume( );
Log.d(TAG, "onResume() - mWasPaused = " + mWasPaused + " : " + SystemClock.elapsedRealtime());
// RESUME needs to be handled only after PAUSE was done
if ( mWasPaused ) {
mHandler.sendMessage( Message.obtain(mHandler, START_BOOT_SPLASH) );
mHandler.sendMessageDelayed( Message.obtain(mHandler, STOP_BOOT_SPLASH), 2000 );
// Activity can just quit as the device is back now
restoreOriginalState();
// Go to "Home-Screen" when device resumes
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
startActivity(intent);
// Destory this activity as Device is resumed & restored to original state
finish();
mStateRestored = true;
}
}
@Override
protected void onPause( ) {
super.onPause( );
Log.d(TAG, "onPause() : " + SystemClock.elapsedRealtime());
mWasPaused = true;
}
@Override
protected void onStop( ) {
super.onStop( );
Log.d(TAG, "onStop() : " + SystemClock.elapsedRealtime());
// Incase, if something stops/kills us before RESUME was handled.
if ( mStateRestored == false ) {
// Activity can just quit as the device is back now
restoreOriginalState();
// Destory this activity as Device is resumed & restored to original state
finish();
mStateRestored = true;
}
}
@Override
protected void onDestroy( ) {
super.onDestroy( );
Log.d(TAG, "onDestroy() : " + SystemClock.elapsedRealtime());
}
/**
* Ensure smooth state for the device to enter SUSPEND smoothly
*/
private void setSuspendState() {
Log.d( TAG, "setSuspendState()");
// Should n't suspend during Monkey-run ( Similar to shutdown scenario )
boolean isDebuggableMonkeyBuild =
SystemProperties.getBoolean("ro.monkey", false);
if (isDebuggableMonkeyBuild) {
Log.d(TAG, "Rejected suspend as monkey is detected to be running. Destroying activity..");
// Destory this activity as Device is NOT suspending
finish();
return;
}
Log.d(TAG, "Notifying thread to start radio shutdown");
forceKillActiveServices();
// Disable keypad , Enable airplane-mode as we are entering RESUME state
setKeypadEnabled(false);
setAirplaneModeEnabled(true);
PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
pm.goToSleep( SystemClock.uptimeMillis() );
}
/**
* Does n't need to do anything wrt Device-Resume., This activity's onResume() is called soon after
* the Device-Resume happens by POWER-key press.,
* So, if the Airplane Mode was earlier enabled by us, lets disable it and bring back the state to
* the Device's original state.
*/
private void restoreOriginalState() {
Log.d(TAG, "restoreOriginalState() : " + SystemClock.elapsedRealtime());
// Enable keypad , Disable airplane-mode as we are entering RESUME state
setKeypadEnabled(true);
setAirplaneModeEnabled(false);
}
/**
* Checks whether any active services are present., Its better to force-stop them( and their originating process'es)
* to ensure smooth sleep (power-collapse) state is entered.,
* One Example - MediaplaybackService., Music Application doesn't handle onPause/suspend properly.,
* Force-Stop == stops the process gracefully and then restarts it in another 5 secs
*/
private void forceKillActiveServices() {
Log.d(TAG, "forceKillActiveServices() : " + SystemClock.elapsedRealtime() );
List<ActivityManager.RunningServiceInfo> runningServices;
ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
// AM's API getRunningServices() requires a ?int? parameter. Here, we are getting first 50 services used/active by user applications.
// Though this actul figure is quite less
runningServices = activityManager.getRunningServices(50);
for (int i = 0; i < runningServices.size(); i++) {
ActivityManager.RunningServiceInfo si = runningServices.get(i);
// Don't touch NON-started services
if (!si.started && si.clientLabel == 0) {
Log.i( TAG, "Ignore [ !si.started && si.clientLabel == 0 ] : Process = " + si.process + " with component " + si.service.getClassName() );
continue;
}
// Don't touch PERSISTENT services. They are already part of system and wont trouble much
if ((si.flags&ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS) != 0) {
Log.i( TAG, "Ignore [ FLAG_PERSISTENT_PROCESS ] : Process = " + si.process + " with component " + si.service.getClassName() + " clientLabel = " + si.clientLabel );
continue;
}
// Don't touch Inputmethod.latin service. If stopped, this (current foreground) activity might be destroyed & recreated.
if ( si.process.startsWith(INPUT_METHOD_SERVICE) ) {
Log.i( TAG, "No POINT in killing System-Input Process = " + si.process + " with component " + si.service.getClassName() + " clientLabel = " + si.clientLabel );
continue;
}
try {
activityManager.forceStopPackage(si.process);
// activityManager.killBackgroundProcesses(si.process);
Log.i( TAG, "forceStopPackage [SUCCESS] : Process = " + si.process + " with component " + si.service.getClassName() + " clientLabel = " + si.clientLabel );
}
catch (Exception e) {
Log.i( TAG, "forceStopPackage [FAIL] ! : Process = " + si.process + " with component " + si.service.getClassName() + " clientLabel = " + si.clientLabel );
}
}
}
/**
* Disable Keypad (Except POWER-Key) when device enters suspend state
*/
private void setKeypadEnabled( boolean on ) {
Log.d( TAG, "setKeypadEnabled() : on = " + on);
String disableStr = (on ? "0" : "1" );
SystemProperties.set( "hw.keyboard.disable" , disableStr );
}
/**
* Enable Airplane-mode when device enters suspend state., Restore to original state when device resumes.
*/
private void setAirplaneModeEnabled( boolean on ) {
Log.d( TAG, "setAirplaneModeEnabled() : on = " + on);
if ( on ) {
int airplaneMode = Settings.System.getInt( getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 0 );
mAirplaneModeSetBy = ( airplaneMode == 0 );
changeAirplaneModeSystemSetting(true);
if ( mAirplaneModeSetBy ) {
Log.d( TAG, "NO airplaneMode set. Lets enable it!");
}
else {
Log.d( TAG, "AirplaneMode already set. Lets not worry :-)");
}
}
else {
if ( mAirplaneModeSetBy ) {
Log.d( TAG, "Suspend has enabled Airplane mode. Lets disable it!" );
changeAirplaneModeSystemSetting(false);
}
else {
Log.d( TAG, "AirplaneMode NOT set by SUSPEND. Lets not worry :-)");
}
}
}
/**
* Change the airplane mode system setting
* Copied from "frameworks/policies/base/phone/com/android/internal/policy/impl/GlobalActions.java"
*/
private void changeAirplaneModeSystemSetting( boolean on ) {
Log.d( TAG, "changeAirplaneModeSystemSetting() : " + SystemClock.elapsedRealtime());
Settings.System.putInt(
getContentResolver(),
Settings.System.AIRPLANE_MODE_ON,
on ? 1 : 0);
Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.putExtra("state", on);
sendBroadcast(intent);
}
}